diff options
author | Kazuki Yamaguchi <k@rhe.jp> | 2016-09-19 15:38:44 +0900 |
---|---|---|
committer | Kazuki Yamaguchi <k@rhe.jp> | 2016-09-28 12:15:14 +0900 |
commit | 1648afef33c1d97fb203c82291b8a61269e85d3b (patch) | |
tree | e6b4315a8aeed890a366cd692ea88b8a9432a68f | |
parent | c3da84ff9af57dc61dff84a64fc01eefdbe21ceb (diff) | |
download | ruby-openssl-1648afef33c1d97fb203c82291b8a61269e85d3b.tar.gz |
asn1: fix out-of-bounds read in decoding constructed objectstopic/asn1-fix-oob-read-constructed
OpenSSL::ASN1.{decode,decode_all,traverse} have a bug of out-of-bounds
read. int_ossl_asn1_decode0_cons() does not give the correct available
length to ossl_asn1_decode() when decoding the inner components of a
constructed object. This can cause out-of-bounds read if a crafted input
given.
Reference: https://hackerone.com/reports/170316
-rw-r--r-- | ext/openssl/ossl_asn1.c | 13 | ||||
-rw-r--r-- | test/test_asn1.rb | 23 |
2 files changed, 29 insertions, 7 deletions
diff --git a/ext/openssl/ossl_asn1.c b/ext/openssl/ossl_asn1.c index 2b8a85f0..90d35c6d 100644 --- a/ext/openssl/ossl_asn1.c +++ b/ext/openssl/ossl_asn1.c @@ -832,19 +832,18 @@ int_ossl_asn1_decode0_cons(unsigned char **pp, long max_len, long length, { VALUE value, asn1data, ary; int infinite; - long off = *offset; + long available_len, off = *offset; infinite = (j == 0x21); ary = rb_ary_new(); - while (length > 0 || infinite) { + available_len = infinite ? max_len : length; + while (available_len > 0) { long inner_read = 0; - value = ossl_asn1_decode0(pp, max_len, &off, depth + 1, yield, &inner_read); + value = ossl_asn1_decode0(pp, available_len, &off, depth + 1, yield, &inner_read); *num_read += inner_read; - max_len -= inner_read; + available_len -= inner_read; rb_ary_push(ary, value); - if (length > 0) - length -= inner_read; if (infinite && NUM2INT(ossl_asn1_get_tag(value)) == V_ASN1_EOC && @@ -935,7 +934,7 @@ ossl_asn1_decode0(unsigned char **pp, long length, long *offset, int depth, if(j & V_ASN1_CONSTRUCTED) { *pp += hlen; off += hlen; - asn1data = int_ossl_asn1_decode0_cons(pp, length, len, &off, depth, yield, j, tag, tag_class, &inner_read); + asn1data = int_ossl_asn1_decode0_cons(pp, length - hlen, len, &off, depth, yield, j, tag, tag_class, &inner_read); inner_read += hlen; } else { diff --git a/test/test_asn1.rb b/test/test_asn1.rb index 612c59a2..ed0013c4 100644 --- a/test/test_asn1.rb +++ b/test/test_asn1.rb @@ -535,6 +535,29 @@ rEzBQ0F9dUyqQ9gyRg8KHhDfv9HzT1d/rnUZMkoombwYBRIUChGCYV0GnJcan2Zm assert_equal(false, asn1.value[3].infinite_length) end + def test_decode_constructed_overread + test = %w{ 31 06 31 02 30 02 05 00 } + # ^ <- invalid + raw = [test.join].pack("H*") + ret = [] + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.traverse(raw) { |x| ret << x } + } + assert_equal 2, ret.size + assert_equal 17, ret[0][6] + assert_equal 17, ret[1][6] + + test = %w{ 31 80 30 03 00 00 } + # ^ <- invalid + raw = [test.join].pack("H*") + ret = [] + assert_raise(OpenSSL::ASN1::ASN1Error) { + OpenSSL::ASN1.traverse(raw) { |x| ret << x } + } + assert_equal 1, ret.size + assert_equal 17, ret[0][6] + end + private def assert_universal(tag, asn1) |