Skip to content

Commit

Permalink
fix #2966 #2967 #2974 - oob read in asn1.c
Browse files Browse the repository at this point in the history
  • Loading branch information
wargio committed Aug 23, 2022
1 parent 4bfa071 commit 41f4d56
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 60 deletions.
12 changes: 10 additions & 2 deletions librz/bin/format/pe/pe_security.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ static const char *PE_(bin_pe_get_claimed_authentihash)(RzBinPEObj *bin) {
return NULL;
}
RASN1Binary *digest = bin->spcinfo->messageDigest.digest;
if (!digest) {
return NULL;
}
return rz_hex_bin2strdup(digest->binary, digest->length);
}

Expand All @@ -19,7 +22,7 @@ static ut64 buf_fwd_hash(const ut8 *buf, ut64 size, void *user) {
}

char *PE_(bin_pe_compute_authentihash)(RzBinPEObj *bin) {
if (!bin->spcinfo) {
if (!bin->spcinfo || !bin->spcinfo->messageDigest.digestAlgorithm.algorithm) {
return NULL;
}

Expand Down Expand Up @@ -131,7 +134,12 @@ int PE_(bin_pe_init_security)(RzBinPEObj *bin) {

if (!bin->cms && cert->wCertificateType == PE_WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
bin->cms = rz_pkcs7_parse_cms(cert->bCertificate, cert->dwLength - 6);
bin->spcinfo = rz_pkcs7_parse_spcinfo(bin->cms);
bin->spcinfo = bin->cms ? rz_pkcs7_parse_spcinfo(bin->cms) : NULL;
}
if (!bin->cms || !bin->spcinfo) {
RZ_FREE(cert->bCertificate);
RZ_FREE(cert);
return false;
}

security_directory->certificates[security_directory->length] = cert;
Expand Down
5 changes: 3 additions & 2 deletions librz/core/cmd/cmd_print.c
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,7 @@ static void cmd_print_fromage(RzCore *core, const char *input, const ut8 *data,
switch (*input) {
case 'a': {
asn1_setformat(input[1] != 'q');
RASN1Object *asn1 = rz_asn1_create_object(data, size, data);
RASN1Object *asn1 = rz_asn1_create_object(data, size);
if (asn1) {
char *res = rz_asn1_to_string(asn1, 0, NULL);
rz_asn1_free_object(asn1);
Expand All @@ -1088,7 +1088,8 @@ static void cmd_print_fromage(RzCore *core, const char *input, const ut8 *data,
} break;
case 'x': // "pFx" x509
{
RX509Certificate *x509 = rz_x509_parse_certificate(rz_asn1_create_object(data, size, data));
RASN1Object *object = rz_asn1_create_object(data, size);
RX509Certificate *x509 = rz_x509_parse_certificate(object);
if (x509) {
RzStrBuf *sb = rz_strbuf_new("");
rz_x509_certificate_dump(x509, NULL, sb);
Expand Down
2 changes: 1 addition & 1 deletion librz/include/rz_util/rz_asn1.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ typedef struct rz_asn1_object_t {
ASN1List list; /* List of objects contained in the sector */
} RASN1Object;

RZ_API RASN1Object *rz_asn1_create_object(const ut8 *buffer, ut32 length, const ut8 *start_pointer);
RZ_API RZ_OWN RASN1Object *rz_asn1_create_object(RZ_NONNULL const ut8 *buffer, ut32 length);
RZ_API RASN1Binary *rz_asn1_create_binary(const ut8 *buffer, ut32 length);
RZ_API RASN1String *rz_asn1_create_string(const char *string, bool allocated, ut32 length);
RZ_API RASN1String *rz_asn1_stringify_bits(const ut8 *buffer, ut32 length);
Expand Down
80 changes: 47 additions & 33 deletions librz/util/asn1.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ RZ_API void asn1_setformat(int fmt) {
ASN1_STD_FORMAT = fmt;
}

static ut32 asn1_ber_indefinite(const ut8 *buffer, ut32 length) {
static ut64 asn1_ber_indefinite(const ut8 *buffer, ut64 length) {
if (!buffer || length < 3) {
return 0;
}
Expand All @@ -25,8 +25,8 @@ static ut32 asn1_ber_indefinite(const ut8 *buffer, ut32 length) {
}
if (next[0] == 0x80 && (next[-1] & ASN1_FORM) == FORM_CONSTRUCTED) {
next--;
int sz = asn1_ber_indefinite(next, end - next);
if (sz < 1) {
st64 sz = (st64)asn1_ber_indefinite(next, end - next);
if (sz < (st64)1) {
break;
}
next += sz;
Expand All @@ -36,9 +36,9 @@ static ut32 asn1_ber_indefinite(const ut8 *buffer, ut32 length) {
return (next - buffer) + 2;
}

static RASN1Object *asn1_parse_header(const ut8 *buffer, ut32 length, const ut8 *start_pointer) {
static RASN1Object *asn1_parse_header(const ut8 *buffer, ut64 length, const ut8 *start_pointer) {
ut8 head, length8, byte;
ut64 length64;
ut64 length64, remaining;
if (!buffer || length < 2) {
return NULL;
}
Expand All @@ -48,76 +48,86 @@ static RASN1Object *asn1_parse_header(const ut8 *buffer, ut32 length, const ut8
return NULL;
}
head = buffer[0];
object->offset = start_pointer ? (buffer - start_pointer) : 0;
object->offset = buffer - start_pointer;
object->klass = head & ASN1_CLASS;
object->form = head & ASN1_FORM;
object->tag = head & ASN1_TAG;
length8 = buffer[1];
remaining = length - 2;
if (length8 & ASN1_LENLONG) {
length64 = 0;
length8 &= ASN1_LENSHORT;
object->sector = buffer + 2;
if (length8 && length8 < length - 2) {
ut8 i8;
if (length8 && length8 < remaining) {
remaining -= length8;
// can overflow.
for (i8 = 0; i8 < length8; i8++) {
for (ut8 i8 = 0; i8 < length8; i8++) {
byte = buffer[2 + i8];
length64 <<= 8;
length64 |= byte;
if (length64 > length) {
if (length64 > remaining) {
// Malformed object - overflow from data ptr
goto out_error;
}
}
object->sector += length8;
} else {
length64 = asn1_ber_indefinite(object->sector, length - 2);
length64 = asn1_ber_indefinite(object->sector, remaining);
if (length64 > remaining) {
// Malformed object - overflow from data ptr
goto out_error;
}
}
object->length = (ut32)length64;
} else {
if (length8 > remaining) {
// Malformed object - overflow from data ptr
goto out_error;
}
object->length = (ut32)length8;
object->sector = buffer + 2;
}

if (object->tag == TAG_BITSTRING && object->sector[0] == 0) {
if (object->length > 0) {
object->sector++; // real sector starts + 1
object->length--;
}
}
if (object->length > length) {
if (object->sector >= (buffer + length)) {
// Malformed object - overflow from data ptr
goto out_error;
}

if (object->tag == TAG_BITSTRING && !object->sector[0] && object->length > 0) {
object->sector++; // real sector starts + 1
object->length--;
}
return object;
out_error:
free(object);
return NULL;
}

static ut32 rz_asn1_count_objects(const ut8 *buffer, ut32 length) {
if (!buffer || !length) {
static ut32 asn1_count_objects(RASN1Object *object) {
if (!object) {
return 0;
}
const ut8 *buffer = object->sector;
ut64 length = object->length;
ut32 counter = 0;
RASN1Object *object = NULL;
RASN1Object *tmp = NULL;
const ut8 *next = buffer;
const ut8 *end = buffer + length;
while (next >= buffer && next < end) {
// i do not care about the offset now.
object = asn1_parse_header(next, end - next, 0);
if (!object || next == object->sector) {
RZ_FREE(object);
tmp = asn1_parse_header(next, end - next, buffer);
if (!tmp || next == tmp->sector) {
RZ_FREE(tmp);
break;
}
next = object->sector + object->length;
next = tmp->sector + tmp->length;
counter++;
RZ_FREE(object);
RZ_FREE(tmp);
}
RZ_FREE(object);
RZ_FREE(tmp);
return counter;
}

RZ_API RASN1Object *rz_asn1_create_object(const ut8 *buffer, ut32 length, const ut8 *start_pointer) {
static RASN1Object *asn1_create_object(const ut8 *buffer, ut64 length, const ut8 *start_pointer) {
RASN1Object *object = asn1_parse_header(buffer, length, start_pointer);
if (object && (object->form == FORM_CONSTRUCTED || object->tag == TAG_BITSTRING || object->tag == TAG_OCTETSTRING)) {
const ut8 *next = object->sector;
Expand All @@ -126,17 +136,16 @@ RZ_API RASN1Object *rz_asn1_create_object(const ut8 *buffer, ut32 length, const
free(object);
return NULL;
}
ut32 count = rz_asn1_count_objects(object->sector, object->length);
ut64 count = asn1_count_objects(object);
if (count > 0) {
object->list.length = count;
object->list.objects = RZ_NEWS0(RASN1Object *, count);
if (!object->list.objects) {
rz_asn1_free_object(object);
return NULL;
}
ut32 i;
for (i = 0; next >= buffer && next < end && i < count; i++) {
RASN1Object *inner = rz_asn1_create_object(next, end - next, start_pointer);
for (ut32 i = 0; next >= buffer && next < end && i < count; i++) {
RASN1Object *inner = asn1_create_object(next, end - next, start_pointer);
if (!inner || next == inner->sector) {
rz_asn1_free_object(inner);
break;
Expand All @@ -149,6 +158,11 @@ RZ_API RASN1Object *rz_asn1_create_object(const ut8 *buffer, ut32 length, const
return object;
}

RZ_API RZ_OWN RASN1Object *rz_asn1_create_object(RZ_NONNULL const ut8 *buffer, ut32 length) {
rz_return_val_if_fail(buffer && length > 0, NULL);
return asn1_create_object(buffer, length, buffer);
}

RZ_API RASN1Binary *rz_asn1_create_binary(const ut8 *buffer, ut32 length) {
if (!buffer || !length) {
return NULL;
Expand Down
4 changes: 2 additions & 2 deletions librz/util/pkcs7.c
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ RZ_API RCMS *rz_pkcs7_parse_cms(const ut8 *buffer, ut32 length) {
if (!container) {
return NULL;
}
object = rz_asn1_create_object(buffer, length, buffer);
object = rz_asn1_create_object(buffer, length);
if (!object || object->list.length < 2 || !object->list.objects ||
!object->list.objects[0] || !object->list.objects[1] ||
object->list.objects[1]->list.length < 1) {
Expand Down Expand Up @@ -689,7 +689,7 @@ RZ_API SpcIndirectDataContent *rz_pkcs7_parse_spcinfo(RCMS *cms) {
free(spcinfo);
return NULL;
}
RASN1Object *object = rz_asn1_create_object(content->binary, content->length, content->binary);
RASN1Object *object = rz_asn1_create_object(content->binary, content->length);
if (!object || object->list.length < 2 || !object->list.objects ||
!object->list.objects[0] || !object->list.objects[1]) {
RZ_FREE_CUSTOM(spcinfo, rz_pkcs7_free_spcinfo);
Expand Down
47 changes: 27 additions & 20 deletions librz/util/x509.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ static bool rz_x509_parse_validity(RX509Validity *validity, RASN1Object *object)
object->tag == TAG_SEQUENCE &&
object->form == FORM_CONSTRUCTED) {
o = object->list.objects[0];
if (o->klass == CLASS_UNIVERSAL && o->form == FORM_PRIMITIVE) {
if (o && o->klass == CLASS_UNIVERSAL && o->form == FORM_PRIMITIVE) {
if (o->tag == TAG_UTCTIME) {
validity->notBefore = rz_asn1_stringify_utctime(o->sector, o->length);
} else if (o->tag == TAG_GENERALIZEDTIME) {
validity->notBefore = rz_asn1_stringify_time(o->sector, o->length);
}
}
o = object->list.objects[1];
if (o->klass == CLASS_UNIVERSAL && o->form == FORM_PRIMITIVE) {
if (o && o->klass == CLASS_UNIVERSAL && o->form == FORM_PRIMITIVE) {
if (o->tag == TAG_UTCTIME) {
validity->notAfter = rz_asn1_stringify_utctime(o->sector, o->length);
} else if (o->tag == TAG_GENERALIZEDTIME) {
Expand Down Expand Up @@ -128,12 +128,12 @@ RZ_API bool rz_x509_parse_extension(RX509Extension *ext, RASN1Object *object) {
if (o && o->tag == TAG_OID) {
ext->extnID = rz_asn1_stringify_oid(o->sector, o->length);
o = object->list.objects[1];
if (o->tag == TAG_BOOLEAN && object->list.length > 2) {
if (o && o->tag == TAG_BOOLEAN && object->list.length > 2) {
// This field is optional (so len must be 3)
ext->critical = o->sector[0] != 0;
o = object->list.objects[2];
}
if (o->tag == TAG_OCTETSTRING) {
if (o && o->tag == TAG_OCTETSTRING) {
ext->extnValue = rz_asn1_create_binary(o->sector, o->length);
}
}
Expand Down Expand Up @@ -170,9 +170,11 @@ RZ_API bool rz_x509_parse_tbscertificate(RX509TBSCertificate *tbsc, RASN1Object
}
elems = object->list.objects;
// Following RFC
if (elems[0]->list.length == 1 &&
if (elems[0] &&
elems[0]->list.length == 1 &&
elems[0]->klass == CLASS_CONTEXT &&
elems[0]->form == FORM_CONSTRUCTED &&
elems[0]->list.objects[0] &&
elems[0]->list.objects[0]->tag == TAG_INTEGER &&
elems[0]->list.objects[0]->length == 1) {
if (object->list.length < 7) {
Expand All @@ -185,7 +187,7 @@ RZ_API bool rz_x509_parse_tbscertificate(RX509TBSCertificate *tbsc, RASN1Object
} else {
tbsc->version = 0;
}
if (shift < object->list.length && elems[shift]->klass == CLASS_UNIVERSAL && elems[shift]->tag == TAG_INTEGER) {
if (elems[shift] && elems[shift]->klass == CLASS_UNIVERSAL && elems[shift]->tag == TAG_INTEGER) {
tbsc->serialNumber = rz_asn1_stringify_integer(elems[shift]->sector, elems[shift]->length);
}
rz_x509_parse_algorithmidentifier(&tbsc->signature, elems[shift + 1]);
Expand All @@ -199,16 +201,10 @@ RZ_API bool rz_x509_parse_tbscertificate(RX509TBSCertificate *tbsc, RASN1Object
continue;
}
if (elems[i]->tag == 1) {
tbsc->issuerUniqueID = rz_asn1_create_binary(object->list.objects[i]->sector, object->list.objects[i]->length);
}
if (!elems[i]) {
continue;
tbsc->issuerUniqueID = rz_asn1_create_binary(elems[i]->sector, elems[i]->length);
}
if (elems[i]->tag == 2) {
tbsc->subjectUniqueID = rz_asn1_create_binary(object->list.objects[i]->sector, object->list.objects[i]->length);
}
if (!elems[i]) {
continue;
tbsc->subjectUniqueID = rz_asn1_create_binary(elems[i]->sector, elems[i]->length);
}
if (tbsc->version == 2 && elems[i]->tag == 3 && elems[i]->form == FORM_CONSTRUCTED) {
rz_x509_parse_extensions(&tbsc->extensions, elems[i]);
Expand All @@ -226,7 +222,12 @@ RZ_API RX509Certificate *rz_x509_parse_certificate(RASN1Object *object) {
if (!cert) {
goto fail;
}
if (object->klass != CLASS_UNIVERSAL || object->form != FORM_CONSTRUCTED || object->list.length != 3) {
if (object->klass != CLASS_UNIVERSAL ||
object->form != FORM_CONSTRUCTED ||
object->list.length != 3 ||
!object->list.objects[0] ||
!object->list.objects[1] ||
!object->list.objects[2]) {
RZ_FREE(cert);
goto fail;
}
Expand Down Expand Up @@ -256,18 +257,21 @@ RZ_API RX509Certificate *rz_x509_parse_certificate2(const ut8 *buffer, ut32 leng
if (!buffer || !length) {
return NULL;
}
object = rz_asn1_create_object(buffer, length, buffer);
object = rz_asn1_create_object(buffer, length);
certificate = rz_x509_parse_certificate(object);
// object freed by rz_x509_parse_certificate
return certificate;
}

RZ_API RX509CRLEntry *rz_x509_parse_crlentry(RASN1Object *object) {
RX509CRLEntry *entry;
if (!object || object->list.length != 2) {
if (!object ||
object->list.length != 2 ||
!object->list.objects[1] ||
!object->list.objects[0]) {
return NULL;
}
entry = (RX509CRLEntry *)malloc(sizeof(RX509CRLEntry));
entry = RZ_NEW0(RX509CRLEntry);
if (!entry) {
return NULL;
}
Expand All @@ -282,12 +286,15 @@ RZ_API RX509CertificateRevocationList *rz_x509_parse_crl(RASN1Object *object) {
if (!object || object->list.length < 4) {
return NULL;
}
crl = (RX509CertificateRevocationList *)malloc(sizeof(RX509CertificateRevocationList));
crl = RZ_NEW0(RX509CertificateRevocationList);
if (!crl) {
return NULL;
}
memset(crl, 0, sizeof(RX509CertificateRevocationList));
elems = object->list.objects;
if (!elems || !elems[0] || !elems[1] || !elems[2] || !elems[3]) {
free(crl);
return NULL;
}
rz_x509_parse_algorithmidentifier(&crl->signature, elems[0]);
rz_x509_parse_name(&crl->issuer, elems[1]);
crl->lastUpdate = rz_asn1_stringify_utctime(elems[2]->sector, elems[2]->length);
Expand Down

0 comments on commit 41f4d56

Please sign in to comment.