11 Commits

Author SHA1 Message Date
Michał Trojnara 97a9ade6ec Release 2.13
Signed-off-by: Michał Trojnara <Michal.Trojnara@stunnel.org>
2026-02-10 22:51:25 +01:00
Michał Trojnara f2f33bb131 Move providers_cleanup() back 2026-02-10 19:10:00 +01:00
Michał Trojnara 202b2c2866 Separate OpenSSL and functional initialization 2026-02-10 18:37:48 +01:00
olszomal 2a5409b7c4 Fix header bounds validation in PE page hash calculation 2026-02-09 16:15:21 +01:00
olszomal 87bce8e372 Fix memory leaks on error paths in PE signing 2026-02-09 16:15:21 +01:00
olszomal f7ace57c81 Validate PE attribute certificate table bounds 2026-02-09 16:15:21 +01:00
olszomal 92f8761b47 Fix heap corruption in PE page hash calculation 2026-02-06 15:41:01 +01:00
Antoni Klajn 09d3312fd9 Fixed integer overflow, integer underflow and Out-of-Bounds Read 2026-02-06 11:39:49 +01:00
olszomal 9d02a20aec Fix unsafe ZIP size handling and allocation checks 2026-02-04 10:13:03 +01:00
olszomal f190ec5d87 Fix double free in ZIP local header 2026-02-04 10:13:03 +01:00
Michał Trojnara 4b30d6be28 Initial 2.12-dev commit 2026-02-03 17:20:29 +01:00
12 changed files with 344 additions and 109 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ on:
env: env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release BUILD_TYPE: Release
version: osslsigncode-2.12 version: osslsigncode-2.13
jobs: jobs:
build: build:
+1 -1
View File
@@ -10,7 +10,7 @@ set(BUILTIN_SOCKET ON CACHE BOOL "") # for static Python
# configure basic project information # configure basic project information
project(osslsigncode project(osslsigncode
VERSION 2.12 VERSION 2.13
DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files" DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files"
HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode" HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode"
LANGUAGES C) LANGUAGES C)
+15
View File
@@ -1,5 +1,20 @@
# osslsigncode change log # osslsigncode change log
### 2.13 (2026.02.10)
**MULTIPLE SECURITY VULNERABILITIES**
This release includes important security fixes. Users are strongly encouraged
to upgrade, as the issues below may be exploitable when processing untrusted
files.
- fixed integer overflows when processing APPX compressed data streams
(by Małgorzata Olszówka)
- fixed double-free vulnerabilities in APPX file processing
(by Małgorzata Olszówka)
- fixed multiple memory corruption issues in PE page hash computation
(by Antoni Klajn (Opera) and Małgorzata Olszówka)
### 2.12 (2026.02.02) ### 2.12 (2026.02.02)
**CRITICAL SECURITY VULNERABILITY** **CRITICAL SECURITY VULNERABILITY**
+49 -5
View File
@@ -1503,6 +1503,7 @@ static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64
if (!get_current_position(bio, &offset)) { if (!get_current_position(bio, &offset)) {
fprintf(stderr, "Unable to get offset\n"); fprintf(stderr, "Unable to get offset\n");
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
header.fileName = NULL;
OPENSSL_free(dataToWrite); OPENSSL_free(dataToWrite);
return 0; /* FAILED */ return 0; /* FAILED */
} }
@@ -1513,6 +1514,7 @@ static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64
if (!BIO_write_ex(bio, dataToWrite + written, toWrite, &check) if (!BIO_write_ex(bio, dataToWrite + written, toWrite, &check)
|| check != toWrite) { || check != toWrite) {
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
header.fileName = NULL;
OPENSSL_free(dataToWrite); OPENSSL_free(dataToWrite);
return 0; /* FAILED */ return 0; /* FAILED */
} }
@@ -1685,6 +1687,8 @@ static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO
out: out:
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
OPENSSL_free(header.extraField); OPENSSL_free(header.extraField);
header.fileName = NULL;
header.extraField = NULL;
return ret; return ret;
} }
@@ -1852,6 +1856,11 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
} }
if (entry->overrideData) { if (entry->overrideData) {
compressedSize = entry->overrideData->compressedSize; compressedSize = entry->overrideData->compressedSize;
/* Validate sizes for safe allocation */
if (compressedSize > (uint64_t)(SIZE_MAX - 1)) {
fprintf(stderr, "Corrupted compressedSize : %" PRIu64"\n", compressedSize);
return 0; /* FAILED */
}
uncompressedSize = entry->overrideData->uncompressedSize; uncompressedSize = entry->overrideData->uncompressedSize;
compressedData = OPENSSL_zalloc(compressedSize + 1); compressedData = OPENSSL_zalloc(compressedSize + 1);
memcpy(compressedData, entry->overrideData->data, compressedSize); memcpy(compressedData, entry->overrideData->data, compressedSize);
@@ -1863,6 +1872,8 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
if (!zipReadLocalHeader(&header, zip, compressedSize)) { if (!zipReadLocalHeader(&header, zip, compressedSize)) {
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
OPENSSL_free(header.extraField); OPENSSL_free(header.extraField);
header.fileName = NULL;
header.extraField = NULL;
return 0; /* FAILED */ return 0; /* FAILED */
} }
if (header.fileNameLen != entry->fileNameLen if (header.fileNameLen != entry->fileNameLen
@@ -1873,14 +1884,20 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
fprintf(stderr, "Local header does not match central directory entry\n"); fprintf(stderr, "Local header does not match central directory entry\n");
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
OPENSSL_free(header.extraField); OPENSSL_free(header.extraField);
header.fileName = NULL;
header.extraField = NULL;
return 0; /* FAILED */ return 0; /* FAILED */
} }
/* we don't really need those */ /* we don't really need those */
OPENSSL_free(header.fileName); OPENSSL_free(header.fileName);
OPENSSL_free(header.extraField); OPENSSL_free(header.extraField);
header.fileName = NULL;
header.extraField = NULL;
if (compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) { /* Validate sizes for safe allocation */
fprintf(stderr, "Corrupted compressedSize : 0x%08" PRIX64 "\n", entry->compressedSize); if (compressedSize > (uint64_t)(SIZE_MAX - 1)
|| compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) {
fprintf(stderr, "Corrupted compressedSize : %" PRIu64"\n", compressedSize);
return 0; /* FAILED */ return 0; /* FAILED */
} }
compressedData = OPENSSL_zalloc(compressedSize + 1); compressedData = OPENSSL_zalloc(compressedSize + 1);
@@ -1899,11 +1916,24 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
*pData = compressedData; *pData = compressedData;
dataSize = compressedSize; dataSize = compressedSize;
} else if (entry->compression == COMPRESSION_DEFLATE) { } else if (entry->compression == COMPRESSION_DEFLATE) {
uint8_t *uncompressedData = OPENSSL_zalloc(uncompressedSize + 1); uint8_t *uncompressedData;
uint64_t destLen = uncompressedSize; uint64_t destLen, sourceLen;
uint64_t sourceLen = compressedSize;
int ret; int ret;
/* Validate sizes for safe allocation */
if (uncompressedSize > (uint64_t)(SIZE_MAX - 1)) {
fprintf(stderr, "Corrupted uncompressedSize : %" PRIu64"\n", uncompressedSize);
return 0; /* FAILED */
}
/* Detect suspicious compression ratio (zip bomb protection) */
if (uncompressedSize > 1024 * 1024 && uncompressedSize / 100 >= compressedSize) {
fprintf(stderr, "Error: suspicious compression ratio\n");
return 0; /* FAILED */
}
uncompressedData = OPENSSL_zalloc(uncompressedSize + 1);
destLen = uncompressedSize;
sourceLen = compressedSize;
ret = zipInflate(uncompressedData, &destLen, compressedData, (uLong *)&sourceLen); ret = zipInflate(uncompressedData, &destLen, compressedData, (uLong *)&sourceLen);
OPENSSL_free(compressedData); OPENSSL_free(compressedData);
@@ -1970,6 +2000,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
header->extraFieldLen = fileGetU16(file); header->extraFieldLen = fileGetU16(file);
/* file name (variable size) */ /* file name (variable size) */
if (header->fileNameLen > 0) { if (header->fileNameLen > 0) {
/* fileNameLen is uint16_t (ZIP spec, 2-byte field),
* so fileNameLen + 1 cannot overflow size_t */
header->fileName = OPENSSL_zalloc(header->fileNameLen + 1); header->fileName = OPENSSL_zalloc(header->fileNameLen + 1);
size = fread(header->fileName, 1, header->fileNameLen, file); size = fread(header->fileName, 1, header->fileNameLen, file);
if (size != header->fileNameLen) { if (size != header->fileNameLen) {
@@ -1981,6 +2013,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
} }
/* extra field (variable size) */ /* extra field (variable size) */
if (header->extraFieldLen > 0) { if (header->extraFieldLen > 0) {
/* extraFieldLen is uint16_t (ZIP spec, 2-byte field),
* so extraFieldLen + 1 cannot overflow size_t */
header->extraField = OPENSSL_zalloc(header->extraFieldLen + 1); header->extraField = OPENSSL_zalloc(header->extraFieldLen + 1);
size = fread(header->extraField, 1, header->extraFieldLen, file); size = fread(header->extraField, 1, header->extraFieldLen, file);
if (size != header->extraFieldLen) { if (size != header->extraFieldLen) {
@@ -2011,6 +2045,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
fprintf(stderr, "The input file is not a valid zip file - flags indicate data descriptor, but data descriptor signature does not match\n"); fprintf(stderr, "The input file is not a valid zip file - flags indicate data descriptor, but data descriptor signature does not match\n");
OPENSSL_free(header->fileName); OPENSSL_free(header->fileName);
OPENSSL_free(header->extraField); OPENSSL_free(header->extraField);
header->fileName = NULL;
header->extraField = NULL;
return 0; /* FAILED */ return 0; /* FAILED */
} }
header->crc32 = fileGetU32(file); header->crc32 = fileGetU32(file);
@@ -2477,6 +2513,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
entry->offsetOfLocalHeader = fileGetU32(file); entry->offsetOfLocalHeader = fileGetU32(file);
/* file name (variable size) */ /* file name (variable size) */
if (entry->fileNameLen > 0) { if (entry->fileNameLen > 0) {
/* fileNameLen is uint16_t (ZIP spec, 2-byte field),
* so fileNameLen + 1 cannot overflow size_t */
entry->fileName = OPENSSL_zalloc(entry->fileNameLen + 1); entry->fileName = OPENSSL_zalloc(entry->fileNameLen + 1);
size = fread(entry->fileName, 1, entry->fileNameLen, file); size = fread(entry->fileName, 1, entry->fileNameLen, file);
if (size != entry->fileNameLen) { if (size != entry->fileNameLen) {
@@ -2487,6 +2525,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
} }
/* extra field (variable size) */ /* extra field (variable size) */
if (entry->extraFieldLen > 0) { if (entry->extraFieldLen > 0) {
/* extraFieldLen is uint16_t (ZIP spec, 2-byte field),
* so extraFieldLen + 1 cannot overflow size_t */
entry->extraField = OPENSSL_zalloc(entry->extraFieldLen + 1); entry->extraField = OPENSSL_zalloc(entry->extraFieldLen + 1);
size = fread(entry->extraField, 1, entry->extraFieldLen, file); size = fread(entry->extraField, 1, entry->extraFieldLen, file);
if (size != entry->extraFieldLen) { if (size != entry->extraFieldLen) {
@@ -2497,6 +2537,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
} }
/* file comment (variable size) */ /* file comment (variable size) */
if (entry->fileCommentLen > 0) { if (entry->fileCommentLen > 0) {
/* fileCommentLen is uint16_t (ZIP spec, 2-byte field),
* so fileCommentLen + 1 cannot overflow size_t */
entry->fileComment = OPENSSL_zalloc(entry->fileCommentLen + 1); entry->fileComment = OPENSSL_zalloc(entry->fileCommentLen + 1);
size = fread(entry->fileComment, 1, entry->fileCommentLen, file); size = fread(entry->fileComment, 1, entry->fileCommentLen, file);
if (size != entry->fileCommentLen) { if (size != entry->fileCommentLen) {
@@ -2635,6 +2677,8 @@ static int readZipEOCDR(ZIP_EOCDR *eocdr, FILE *file)
} }
#endif #endif
if (eocdr->commentLen > 0) { if (eocdr->commentLen > 0) {
/* ZIP_EOCDR commentLen is uint16_t (ZIP spec, 2-byte field),
* so fileCommentLen + 1 cannot overflow size_t */
eocdr->comment = OPENSSL_zalloc(eocdr->commentLen + 1); eocdr->comment = OPENSSL_zalloc(eocdr->commentLen + 1);
size = fread(eocdr->comment, 1, eocdr->commentLen, file); size = fread(eocdr->comment, 1, eocdr->commentLen, file);
if (size != eocdr->commentLen) { if (size != eocdr->commentLen) {
+2 -1
View File
@@ -342,7 +342,8 @@ static int cab_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
const u_char *p = content_val->data; const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) { if (idc) {
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) { if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
return 0; /* FAILED */ return 0; /* FAILED */
} }
+2 -5
View File
@@ -390,15 +390,12 @@ static int cat_print_content_member_digest(ASN1_TYPE *content)
idc = d2i_SpcIndirectDataContent(NULL, &data, ASN1_STRING_length(value)); idc = d2i_SpcIndirectDataContent(NULL, &data, ASN1_STRING_length(value));
if (!idc) if (!idc)
return 0; /* FAILED */ return 0; /* FAILED */
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) { if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
return 0; /* FAILED */ return 0; /* FAILED */
} }
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
if (mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */
}
printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype)); printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype));
print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype))); print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype)));
return 1; /* OK */ return 1; /* OK */
+35 -27
View File
@@ -548,33 +548,6 @@ SpcLink *spc_link_obsolete_get(void)
return link; return link;
} }
/*
* Safely extract digest from SpcIndirectDataContent
* [in] idc: parsed SpcIndirectDataContent
* [out] mdbuf: output buffer (must be EVP_MAX_MD_SIZE bytes)
* [out] mdtype: digest algorithm's NID
* [returns] -1 on error or digest length on success
*/
int spc_extract_digest_safe(SpcIndirectDataContent *idc,
u_char *mdbuf, int *mdtype)
{
int digest_len;
if (!idc || !idc->messageDigest || !idc->messageDigest->digest ||
!idc->messageDigest->digestAlgorithm) {
fprintf(stderr, "Missing digest data\n");
return -1;
}
digest_len = idc->messageDigest->digest->length;
if (digest_len <= 0 || digest_len > EVP_MAX_MD_SIZE) {
fprintf(stderr, "Invalid digest length: %d\n", digest_len);
return -1;
}
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)digest_len);
*mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
return digest_len;
}
/* /*
* [in] mdbuf, cmdbuf: message digests * [in] mdbuf, cmdbuf: message digests
* [in] mdtype: message digest algorithm type * [in] mdtype: message digest algorithm type
@@ -590,6 +563,37 @@ int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype)
return mdok; return mdok;
} }
/*
* Safely extract digest from SpcIndirectDataContent with bounds checking.
* This function validates that the digest length from the ASN.1 structure
* does not exceed the destination buffer size, preventing buffer overflows
* from maliciously crafted signatures.
* [in] idc: parsed SpcIndirectDataContent structure
* [out] mdbuf: output buffer (must be at least EVP_MAX_MD_SIZE bytes)
* [out] mdtype: digest algorithm NID
* [returns] digest length on success, -1 on error
*/
int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype)
{
int digest_len;
if (!idc || !idc->messageDigest || !idc->messageDigest->digest ||
!idc->messageDigest->digestAlgorithm) {
return -1; /* FAILED */
}
digest_len = idc->messageDigest->digest->length;
/* Validate digest length to prevent buffer overflow */
if (digest_len <= 0 || digest_len > EVP_MAX_MD_SIZE) {
fprintf(stderr, "Invalid digest length in signature: %d (expected 1-%d)\n",
digest_len, EVP_MAX_MD_SIZE);
return -1; /* FAILED */
}
*mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)digest_len);
return digest_len; /* OK */
}
/* /*
* Helper functions * Helper functions
*/ */
@@ -645,6 +649,10 @@ static int spc_indirect_data_content_create(u_char **blob, int *len, FILE_FORMAT
idc->data->value->type = V_ASN1_SEQUENCE; idc->data->value->type = V_ASN1_SEQUENCE;
idc->data->value->value.sequence = ASN1_STRING_new(); idc->data->value->value.sequence = ASN1_STRING_new();
idc->data->type = ctx->format->data_blob_get(&p, &l, ctx); idc->data->type = ctx->format->data_blob_get(&p, &l, ctx);
if (!idc->data->type) {
SpcIndirectDataContent_free(idc);
return 0; /* FAILED */
}
idc->data->value->value.sequence->data = p; idc->data->value->value.sequence->data = p;
idc->data->value->value.sequence->length = l; idc->data->value->value.sequence->length = l;
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype); idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype);
+1 -2
View File
@@ -24,9 +24,8 @@ int is_content_type(PKCS7 *p7, const char *objid);
MsCtlContent *ms_ctl_content_get(PKCS7 *p7); MsCtlContent *ms_ctl_content_get(PKCS7 *p7);
ASN1_TYPE *catalog_content_get(CatalogAuthAttr *attribute); ASN1_TYPE *catalog_content_get(CatalogAuthAttr *attribute);
SpcLink *spc_link_obsolete_get(void); SpcLink *spc_link_obsolete_get(void);
int spc_extract_digest_safe(SpcIndirectDataContent *idc,
u_char *mdbuf, int *mdtype);
int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype); int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype);
int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype);
/* /*
Local Variables: Local Variables:
+2 -1
View File
@@ -419,7 +419,8 @@ static int msi_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
const u_char *p = content_val->data; const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) { if (idc) {
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) { if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
return 0; /* FAILED */ return 0; /* FAILED */
} }
+32 -25
View File
@@ -3082,12 +3082,8 @@ static int verify_content_member_digest(FILE_FORMAT_CTX *ctx, ASN1_TYPE *content
fprintf(stderr, "Failed to extract SpcIndirectDataContent data\n"); fprintf(stderr, "Failed to extract SpcIndirectDataContent data\n");
return 1; /* FAILED */ return 1; /* FAILED */
} }
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) { if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
SpcIndirectDataContent_free(idc); fprintf(stderr, "Failed to extract message digest from signature\n\n");
return 1; /* FAILED */
}
if (mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n");
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
return 1; /* FAILED */ return 1; /* FAILED */
} }
@@ -5082,7 +5078,7 @@ static void engine_control_set(GLOBAL_OPTIONS *options, const char *arg)
} }
#endif /* OPENSSL_NO_ENGINE */ #endif /* OPENSSL_NO_ENGINE */
int main(int argc, char **argv) static int main_execute(int argc, char **argv)
{ {
FILE_FORMAT_CTX *ctx = NULL; FILE_FORMAT_CTX *ctx = NULL;
GLOBAL_OPTIONS options; GLOBAL_OPTIONS options;
@@ -5094,24 +5090,6 @@ int main(int argc, char **argv)
/* reset options */ /* reset options */
memset(&options, 0, sizeof(GLOBAL_OPTIONS)); memset(&options, 0, sizeof(GLOBAL_OPTIONS));
/* Set up OpenSSL */
if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
| OPENSSL_INIT_ADD_ALL_CIPHERS
| OPENSSL_INIT_ADD_ALL_DIGESTS
| OPENSSL_INIT_LOAD_CONFIG, NULL))
DO_EXIT_0("Failed to init crypto\n");
/* create some MS Authenticode OIDS we need later on */
if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, NULL, NULL)
/* PKCS9_COUNTER_SIGNATURE exists as OpenSSL OBJ_pkcs9_countersignature */
|| !OBJ_create(MS_JAVA_SOMETHING, NULL, NULL)
|| !OBJ_create(SPC_SP_OPUS_INFO_OBJID, NULL, NULL)
|| !OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL)
|| !OBJ_create(SPC_UNAUTHENTICATED_DATA_BLOB_OBJID, NULL, NULL)
|| !OBJ_create(SPC_RFC3161_OBJID, NULL, NULL)
|| !OBJ_create(PKCS9_SEQUENCE_NUMBER, NULL, NULL))
DO_EXIT_0("Failed to create objects\n");
/* commands and options initialization */ /* commands and options initialization */
if (!main_configure(argc, argv, &options)) if (!main_configure(argc, argv, &options))
goto err_cleanup; goto err_cleanup;
@@ -5359,6 +5337,35 @@ err_cleanup:
return ret; return ret;
} }
int main(int argc, char **argv)
{
int ret = -1;
/* one-time OpenSSL initialization */
if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
| OPENSSL_INIT_ADD_ALL_CIPHERS
| OPENSSL_INIT_ADD_ALL_DIGESTS
| OPENSSL_INIT_LOAD_CONFIG, NULL))
DO_EXIT_0("Failed to init crypto\n");
/* create some MS Authenticode OIDS we need later on */
if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, NULL, NULL)
/* PKCS9_COUNTER_SIGNATURE exists as OpenSSL OBJ_pkcs9_countersignature */
|| !OBJ_create(MS_JAVA_SOMETHING, NULL, NULL)
|| !OBJ_create(SPC_SP_OPUS_INFO_OBJID, NULL, NULL)
|| !OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL)
|| !OBJ_create(SPC_UNAUTHENTICATED_DATA_BLOB_OBJID, NULL, NULL)
|| !OBJ_create(SPC_RFC3161_OBJID, NULL, NULL)
|| !OBJ_create(PKCS9_SEQUENCE_NUMBER, NULL, NULL))
DO_EXIT_0("Failed to create objects\n");
/* perform the requested operation */
ret = main_execute(argc, argv);
err_cleanup:
return ret;
}
/* /*
Local Variables: Local Variables:
c-basic-offset: 4 c-basic-offset: 4
+202 -40
View File
@@ -163,8 +163,10 @@ static ASN1_OBJECT *pe_spc_image_data_get(u_char **p, int *plen, FILE_FORMAT_CTX
if (EVP_MD_size(ctx->options->md) > EVP_MD_size(EVP_sha1())) if (EVP_MD_size(ctx->options->md) > EVP_MD_size(EVP_sha1()))
phtype = NID_sha256; phtype = NID_sha256;
link = pe_page_hash_link_get(ctx, phtype); link = pe_page_hash_link_get(ctx, phtype);
if (!link) if (!link) {
SpcPeImageData_free(pid);
return NULL; /* FAILED */ return NULL; /* FAILED */
}
pid->file = link; pid->file = link;
} else { } else {
pid->file = spc_link_obsolete_get(); pid->file = spc_link_obsolete_get();
@@ -255,7 +257,9 @@ static int pe_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
return 0; /* FAILED */ return 0; /* FAILED */
} }
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) { if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
OPENSSL_free(ph);
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
return 0; /* FAILED */ return 0; /* FAILED */
} }
@@ -402,6 +406,7 @@ static PKCS7 *pe_pkcs7_signature_new(FILE_FORMAT_CTX *ctx, BIO *hash)
content = spc_indirect_data_content_get(hash, ctx); content = spc_indirect_data_content_get(hash, ctx);
if (!content) { if (!content) {
fprintf(stderr, "Failed to get spcIndirectDataContent\n"); fprintf(stderr, "Failed to get spcIndirectDataContent\n");
PKCS7_free(p7);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!sign_spc_indirect_data_content(p7, content)) { if (!sign_spc_indirect_data_content(p7, content)) {
@@ -914,12 +919,25 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
uint16_t nsections, opthdr_size; uint16_t nsections, opthdr_size;
uint32_t alignment, pagesize, hdrsize; uint32_t alignment, pagesize, hdrsize;
uint32_t rs, ro, l, lastpos = 0; uint32_t rs, ro, l, lastpos = 0;
int pphlen, phlen, i, pi = 1; int mdlen, pphlen, phlen, i, pi = 1;
size_t written; size_t written, off, sect_off, sect_tbl, need;
u_char *res, *zeroes; u_char *res = NULL, *zeroes = NULL;
char *sections; char *sections;
const EVP_MD *md = EVP_get_digestbynid(phtype); const EVP_MD *md = EVP_get_digestbynid(phtype);
BIO *bhash; BIO *bhash = NULL;
uint32_t filebound;
size_t pphlen_sz, sections_factor;
if (rphlen == NULL || ctx == NULL || ctx->options == NULL || ctx->pe_ctx == NULL
|| ctx->options->indata == NULL)
return NULL;
if (md == NULL)
return NULL;
mdlen = EVP_MD_size(md);
if (mdlen <= 0)
return NULL;
/* NumberOfSections indicates the size of the section table, /* NumberOfSections indicates the size of the section table,
* which immediately follows the headers, can be up to 65535 under Vista and later */ * which immediately follows the headers, can be up to 65535 under Vista and later */
@@ -961,10 +979,46 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
fprintf(stderr, "Corrupted optional header size: 0x%08X\n", opthdr_size); fprintf(stderr, "Corrupted optional header size: 0x%08X\n", opthdr_size);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
pphlen = 4 + EVP_MD_size(md); /* Validate that pagesize >= hdrsize to prevent integer underflow */
phlen = pphlen * (3 + (int)nsections + (int)(ctx->pe_ctx->fileend / pagesize)); if (pagesize < hdrsize) {
fprintf(stderr, "Page size (0x%08X) is smaller than header size (0x%08X)\n",
pagesize, hdrsize);
return NULL; /* FAILED */
}
pphlen = 4 + mdlen;
/* Compute an upper bound for result size and guard overflow */
pphlen_sz = (size_t)pphlen;
sections_factor = 3 + (size_t)nsections + ((size_t)ctx->pe_ctx->fileend / pagesize);
if (sections_factor > SIZE_MAX / pphlen_sz) {
fprintf(stderr, "Page hash allocation size would overflow\n");
return NULL; /* FAILED */
}
phlen = (int)(pphlen_sz * sections_factor);
/* Sanity limit - page hash shouldn't exceed reasonable size (16 MB) */
if (phlen < 0 || (size_t)phlen > SIZE_16M) {
fprintf(stderr, "Page hash size exceeds limit: %d\n", phlen);
return NULL; /* FAILED */
}
/* Determine the file boundary for section data validation */
filebound = ctx->pe_ctx->sigpos ? ctx->pe_ctx->sigpos : ctx->pe_ctx->fileend;
/* Validate section table bounds before reading section headers */
sect_off = (size_t)ctx->pe_ctx->header_size + 24u + (size_t)opthdr_size;
sect_tbl = (size_t)nsections * 40u;
if (sect_off > (size_t)filebound || sect_tbl > (size_t)filebound - sect_off) {
fprintf(stderr, "Section table out of bounds: off=%zu size=%zu filebound=%u\n",
sect_off, sect_tbl, filebound);
return NULL; /* FAILED */
}
sections = (char *)ctx->options->indata + sect_off;
bhash = BIO_new(BIO_f_md()); bhash = BIO_new(BIO_f_md());
if (bhash == NULL)
return NULL;
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual" #pragma GCC diagnostic ignored "-Wcast-qual"
@@ -977,7 +1031,10 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
BIO_push(bhash, BIO_new(BIO_s_null())); if (BIO_push(bhash, BIO_new(BIO_s_null())) == NULL) {
BIO_free_all(bhash);
return NULL;
}
if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written) if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written)
|| written != ctx->pe_ctx->header_size + 88) { || written != ctx->pe_ctx->header_size + 88) {
BIO_free_all(bhash); BIO_free_all(bhash);
@@ -989,36 +1046,84 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
BIO_free_all(bhash); BIO_free_all(bhash);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!BIO_write_ex(bhash, off = ctx->pe_ctx->header_size + 160 + (size_t)ctx->pe_ctx->pe32plus * 16;
ctx->options->indata + ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16, if (hdrsize < off || hdrsize > filebound) {
hdrsize - (ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16), &written) BIO_free_all(bhash);
|| written != hdrsize - (ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16)) { return NULL; /* FAILED: header too small */
}
if (!BIO_write_ex(bhash, ctx->options->indata + off, (size_t)hdrsize - off, &written)
|| written != hdrsize - off) {
BIO_free_all(bhash); BIO_free_all(bhash);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (pagesize < hdrsize) {
BIO_free_all(bhash);
return NULL; /* FAILED: header larger than page */
}
zeroes = OPENSSL_zalloc((size_t)pagesize); zeroes = OPENSSL_zalloc((size_t)pagesize);
if (!BIO_write_ex(bhash, zeroes, pagesize - hdrsize, &written) if (zeroes == NULL) {
|| written != pagesize - hdrsize) { BIO_free_all(bhash);
return NULL; /* FAILED */
}
if (!BIO_write_ex(bhash, zeroes, (size_t)pagesize - (size_t)hdrsize, &written)
|| written != (size_t)pagesize - (size_t)hdrsize) {
BIO_free_all(bhash); BIO_free_all(bhash);
OPENSSL_free(zeroes); OPENSSL_free(zeroes);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
res = OPENSSL_malloc((size_t)phlen); res = OPENSSL_malloc((size_t)phlen);
if (res == NULL) {
BIO_free_all(bhash);
OPENSSL_free(zeroes);
return NULL; /* FAILED */
}
memset(res, 0, 4); memset(res, 0, 4);
BIO_gets(bhash, (char*)res + 4, EVP_MD_size(md)); if (BIO_gets(bhash, (char *)res + 4, mdlen) != mdlen) {
BIO_free_all(bhash);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
BIO_free_all(bhash); BIO_free_all(bhash);
sections = ctx->options->indata + ctx->pe_ctx->header_size + 24 + opthdr_size; bhash = NULL;
for (i=0; i<nsections; i++) {
/* Resource Table address and size */ for (i = 0; i < (int)nsections; i++) {
/* SizeOfRawData and PointerToRawData from section header */
rs = GET_UINT32_LE(sections + 16); rs = GET_UINT32_LE(sections + 16);
ro = GET_UINT32_LE(sections + 20); ro = GET_UINT32_LE(sections + 20);
if (rs == 0 || rs >= UINT32_MAX) { if (rs == 0) {
sections += 40; sections += 40;
continue; continue;
} }
for (l=0; l<rs; l+=pagesize, pi++) { /* Validate section bounds against file size to prevent OOB read */
PUT_UINT32_LE(ro + l, res + pi*pphlen); if (ro >= filebound || rs > filebound - ro) {
fprintf(stderr, "Section %d has invalid bounds: offset=0x%08X, size=0x%08X, fileend=0x%08X\n",
i, ro, rs, filebound);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
for (l = 0; l < rs; l += pagesize, pi++) {
need = (size_t)(pi + 1) * (size_t)pphlen;
/* Prevent OOB write into res if pi grows beyond allocated factor */
if (need > (size_t)phlen) {
fprintf(stderr, "Page hash buffer overflow prevented: pi=%d need=%zu phlen=%d\n",
pi, need, phlen);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
PUT_UINT32_LE(ro + l, res + (size_t)pi * (size_t)pphlen);
bhash = BIO_new(BIO_f_md()); bhash = BIO_new(BIO_f_md());
if (bhash == NULL) {
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL;
}
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual" #pragma GCC diagnostic ignored "-Wcast-qual"
@@ -1033,17 +1138,24 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
#if defined(__GNUC__) #if defined(__GNUC__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
BIO_push(bhash, BIO_new(BIO_s_null())); if (BIO_push(bhash, BIO_new(BIO_s_null())) == NULL) {
if (rs - l < pagesize) { BIO_free_all(bhash);
if (!BIO_write_ex(bhash, ctx->options->indata + ro + l, rs - l, &written) OPENSSL_free(zeroes);
|| written != rs - l) { OPENSSL_free(res);
return NULL;
}
if (l < rs && rs - l < pagesize) {
size_t tail = (size_t)(rs - l);
if (!BIO_write_ex(bhash, ctx->options->indata + ro + l, tail, &written)
|| written != tail) {
BIO_free_all(bhash); BIO_free_all(bhash);
OPENSSL_free(zeroes); OPENSSL_free(zeroes);
OPENSSL_free(res); OPENSSL_free(res);
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
if (!BIO_write_ex(bhash, zeroes, pagesize - (rs - l), &written) if (!BIO_write_ex(bhash, zeroes, pagesize - tail, &written)
|| written != pagesize - (rs - l)) { || written != pagesize - tail) {
BIO_free_all(bhash); BIO_free_all(bhash);
OPENSSL_free(zeroes); OPENSSL_free(zeroes);
OPENSSL_free(res); OPENSSL_free(res);
@@ -1058,17 +1170,35 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
return NULL; /* FAILED */ return NULL; /* FAILED */
} }
} }
BIO_gets(bhash, (char*)res + pi*pphlen + 4, EVP_MD_size(md)); if (BIO_gets(bhash, (char *)res + (size_t)pi * (size_t)pphlen + 4, mdlen) != mdlen) {
BIO_free_all(bhash);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
BIO_free_all(bhash); BIO_free_all(bhash);
bhash = NULL;
} }
lastpos = ro + rs; lastpos = ro + rs;
sections += 40; sections += 40;
} }
PUT_UINT32_LE(lastpos, res + pi*pphlen); /* Final entry */
memset(res + pi*pphlen + 4, 0, (size_t)EVP_MD_size(md)); need = (size_t)(pi + 1) * (size_t)pphlen;
if (need > (size_t)phlen) {
fprintf(stderr, "Page hash buffer overflow prevented at final entry: pi=%d need=%zu phlen=%d\n",
pi, need, phlen);
OPENSSL_free(zeroes);
OPENSSL_free(res);
return NULL; /* FAILED */
}
PUT_UINT32_LE(lastpos, res + (size_t)pi * (size_t)pphlen);
memset(res + (size_t)pi * (size_t)pphlen + 4, 0, (size_t)mdlen);
pi++; pi++;
OPENSSL_free(zeroes); OPENSSL_free(zeroes);
*rphlen = pi*pphlen; *rphlen = pi * pphlen;
return res; return res;
} }
@@ -1088,6 +1218,10 @@ static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int
if (!ph) if (!ph)
return 1; /* OK */ return 1; /* OK */
cph = pe_page_hash_calc(&cphlen, ctx, phtype); cph = pe_page_hash_calc(&cphlen, ctx, phtype);
if (!cph) {
fprintf(stderr, "Page hash verification failed: could not calculate page hash\n");
return 0; /* FAILED */
}
mdok = (phlen == cphlen) && !memcmp(ph, cph, (size_t)phlen); mdok = (phlen == cphlen) && !memcmp(ph, cph, (size_t)phlen);
printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype)); printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype));
if (ctx->options->verbose) { if (ctx->options->verbose) {
@@ -1190,7 +1324,8 @@ static int pe_check_file(FILE_FORMAT_CTX *ctx)
{ {
uint32_t real_pe_checksum, sum = 0; uint32_t real_pe_checksum, sum = 0;
if (!ctx) { if (ctx == NULL || ctx->pe_ctx == NULL || ctx->options == NULL
|| ctx->options->indata == NULL) {
fprintf(stderr, "Init error\n"); fprintf(stderr, "Init error\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
@@ -1202,25 +1337,52 @@ static int pe_check_file(FILE_FORMAT_CTX *ctx)
printf("Calculated PE checksum: %08X\n", real_pe_checksum); printf("Calculated PE checksum: %08X\n", real_pe_checksum);
printf("Warning: invalid PE checksum\n"); printf("Warning: invalid PE checksum\n");
} }
/* Signature directory bounds */
if (ctx->pe_ctx->sigpos == 0 || ctx->pe_ctx->siglen == 0 if (ctx->pe_ctx->sigpos == 0 || ctx->pe_ctx->siglen == 0
|| ctx->pe_ctx->sigpos > ctx->pe_ctx->fileend) { || ctx->pe_ctx->sigpos > ctx->pe_ctx->fileend
|| ctx->pe_ctx->siglen > ctx->pe_ctx->fileend - ctx->pe_ctx->sigpos) {
fprintf(stderr, "No signature found\n"); fprintf(stderr, "No signature found\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
/* /*
* Validate WIN_CERTIFICATE chain.
* If the sum of the rounded dwLength values does not equal the Size value, * If the sum of the rounded dwLength values does not equal the Size value,
* then either the attribute certificate table or the Size field is corrupted. * then either the attribute certificate table or the Size field is corrupted.
*/ */
while (sum < ctx->pe_ctx->siglen) { while (sum < ctx->pe_ctx->siglen) {
uint32_t len = GET_UINT32_LE(ctx->options->indata + ctx->pe_ctx->sigpos + sum); uint32_t len, off;
if (ctx->pe_ctx->siglen - len > 8) {
/* Prevent overflow in sigpos + sum */
if (sum > UINT32_MAX - ctx->pe_ctx->sigpos) {
fprintf(stderr, "Corrupted attribute certificate table\n"); fprintf(stderr, "Corrupted attribute certificate table\n");
fprintf(stderr, "Attribute certificate table size : %08X\n", ctx->pe_ctx->siglen);
fprintf(stderr, "Attribute certificate entry length: %08X\n\n", len);
return 0; /* FAILED */ return 0; /* FAILED */
} }
/* quadword align data */ off = ctx->pe_ctx->sigpos + sum;
len += len % 8 ? 8 - len % 8 : 0;
/* Need at least 4 bytes to read dwLength */
if (off > ctx->pe_ctx->fileend || ctx->pe_ctx->fileend - off < 4) {
fprintf(stderr, "Corrupted attribute certificate table\n");
return 0; /* FAILED */
}
len = GET_UINT32_LE(ctx->options->indata + off);
/* dwLength must include the 8-byte WIN_CERTIFICATE header */
if (len < 8 || len > ctx->pe_ctx->siglen - sum || len > ctx->pe_ctx->fileend - off) {
fprintf(stderr, "Corrupted attribute certificate table\n");
return 0; /* FAILED */
}
/* Quadword align data */
if (len % 8) {
uint32_t pad = 8 - (len % 8);
/* Ensure quadword alignment does not overflow or exceed remaining table size */
if (pad > ctx->pe_ctx->siglen - sum - len) {
fprintf(stderr, "Corrupted attribute certificate table\n");
return 0; /* FAILED */
}
len += pad;
}
sum += len; sum += len;
} }
if (sum != ctx->pe_ctx->siglen) { if (sum != ctx->pe_ctx->siglen) {
+2 -1
View File
@@ -294,7 +294,8 @@ static int script_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
const u_char *p = content_val->data; const u_char *p = content_val->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length); SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
if (idc) { if (idc) {
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) { if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
fprintf(stderr, "Failed to extract message digest from signature\n\n");
SpcIndirectDataContent_free(idc); SpcIndirectDataContent_free(idc);
return 0; /* FAILED */ return 0; /* FAILED */
} }