Project: exiv2 (exiv2/exiv2), language: c++
Vulnerability instance: exiv2.cve-2021-29458
Sanitizer: address

================= Bug Report (1/2) ==================
## Source: GitHub Advisory
## URL: https://github.com/Exiv2/exiv2/security/advisories/GHSA-57jj-75fm-9rq5
## Description:
Advisory ID: GHSA-57jj-75fm-9rq5
Title: Out-of-bounds read in Exiv2::Internal::CrwMap::encode

Description:
Impact
An out-of-bounds read was found in Exiv2 versions v0.27.3 and earlier. Exiv2 is a command-line utility and C++ library for reading, writing, deleting, and modifying the metadata of image files. The out-of-bounds read is triggered when Exiv2 is used to write metadata into a crafted image file. An attacker could potentially exploit the vulnerability to cause a denial of service by crashing Exiv2, if they can trick the victim into running Exiv2 on a crafted image file.
Note that this bug is only triggered when writing the metadata, which is a less frequently used Exiv2 operation than reading the metadata. For example, to trigger the bug in the Exiv2 command-line application, you need to add an extra command-line argument such as insert.
Patches
The bug is fixed in version v0.27.4.
References
Issue: #1530
Fix: #1536
For more information
Please see our security policy for information about Exiv2 security.


References:
https://github.com/Exiv2/exiv2/issues/1530
https://github.com/Exiv2/exiv2/pull/1536
https://github.com/Exiv2/exiv2/security/policy

================= Bug Report (2/2) ==================
## Source: GitHub Issue
## URL: https://github.com/Exiv2/exiv2/issues/1530
## Description:
Issue: Exiv2/exiv2#1530
Title: heap-buffer-overflow Read in Exiv2::Internal::CrwMap::encode
State: closed
Created by: henices
Created at: 2021-04-08 07:27:55+00:00
Labels: security (crash)

Issue Body:
**VERSION**
exiv 2 0.27.4.1
https://github.com/Exiv2/exiv2/tree/0.27-maintenance

**REPRODUCE**

Compile exiv2 with asan:

```
CC=clang CXX=clang++ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-fsanitize=address" \
-DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" \
-DCMAKE_MODULE_LINKER_FLAGS="-fsanitize=address"
```

Dowload testcases:

https://github.com/henices/pocs/raw/master/tests_1bd0a5f4935b053f33ac00f931dde1f47a043487
https://github.com/henices/pocs/raw/master/tests_1bd0a5f4935b053f33ac00f931dde1f47a043487.exv

Run command:  `exiv2 in tests_1bd0a5f4935b053f33ac00f931dde1f47a043487`

```
=================================================================
==119384==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x62600000585e at pc 0x0000004c4d0a bp 0x7ffef1036370 sp 0x7ffef1035b20
READ of size 4294967293 at 0x62600000585e thread T0
    #0 0x4c4d09 in __asan_memcpy (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x4c4d09)
    #1 0x7f40c9907d88 in Exiv2::Internal::CrwMap::encode0x1810(Exiv2::Image const&, Exiv2::Internal::CrwMapping const*, Exiv2::Internal::CiffHeader*) (/home/henices/tests/exiv2/build_asan/lib/libexiv2.so.27+0x4c8d88)
    #2 0x7f40c9911007 in Exiv2::Internal::CrwMap::encode(Exiv2::Internal::CiffHeader*, Exiv2::Image const&) (/home/henices/tests/exiv2/build_asan/lib/libexiv2.so.27+0x4d2007)
    #3 0x7f40c9769376 in Exiv2::CrwImage::writeMetadata() (/home/henices/tests/exiv2/build_asan/lib/libexiv2.so.27+0x32a376)
    #4 0x541653 in (anonymous namespace)::metacopy(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int, bool) (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x541653)
    #5 0x545049 in Action::Insert::run(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x545049)
    #6 0x4fddf3 in main (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x4fddf3)
    #7 0x7f40c8ede1e1 in __libc_start_main /usr/src/debug/glibc-2.32-37-g760e1d2878/csu/../csu/libc-start.c:314:16
    #8 0x4224cd in _start (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x4224cd)

0x62600000585e is located 0 bytes to the right of 10078-byte region [0x626000003100,0x62600000585e)
allocated by thread T0 here:
    #0 0x4fad47 in operator new[](unsigned long) (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x4fad47)
    #1 0x7f40c98688b1 in Exiv2::DataBuf::alloc(long) (/home/henices/tests/exiv2/build_asan/lib/libexiv2.so.27+0x4298b1)
    #2 0x541653 in (anonymous namespace)::metacopy(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, int, bool) (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x541653)
    #3 0x545049 in Action::Insert::run(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x545049)
    #4 0x4fddf3 in main (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x4fddf3)
    #5 0x7f40c8ede1e1 in __libc_start_main /usr/src/debug/glibc-2.32-37-g760e1d2878/csu/../csu/libc-start.c:314:16

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/henices/tests/exiv2/build_asan/bin/exiv2+0x4c4d09) in __asan_memcpy
Shadow bytes around the buggy address:
  0x0c4c7fff8ab0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c4c7fff8ac0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c4c7fff8ad0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c4c7fff8ae0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c4c7fff8af0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c4c7fff8b00: 00 00 00 00 00 00 00 00 00 00 00[06]fa fa fa fa
  0x0c4c7fff8b10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4c7fff8b20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4c7fff8b30: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4c7fff8b40: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c4c7fff8b50: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==119384==ABORTING
```

Credit: Zhen Zhou of NSFOCUS Security Team


Comments:
Comment by clanmills on 2021-04-08 12:45:55+00:00:
What is your plan here?  This is the third similar CVE in three days.  Exiv2 v0.27.4 is scheduled to ship on 2021-05-22.  Are you planning to continuously bombard us with CVEs for weeks and months?

Is it possible to have a Zoom meeting to discuss your intention and how we can cooperate?

---

Comment by henices on 2021-04-09 00:42:23+00:00:
@clanmills  Thanks for your hard work to make exiv2 better.  I indeed have several other exiv2 security bugs,  but I don't submit all the bugs at the same time, I can't agree with the strong word bombard. Security testing for exiv2 also takes a lot time, if your guys don't like to see these kind of bugs, feel free to them, I will never submit them again.

I don't know is there a deadline for exiv2 release schedule, sorry for the inconvience.

---

Comment by clanmills on 2021-04-09 07:46:36+00:00:
Thank You @henices for the courtesy of your reply.  And thank you for opening issues on GitHub about these matters.  That's very helpful.  The sooner Team Exiv2 knows about these matters, the sooner they can be fixed.

Team Exiv2 agrees that knowing about those issues and fixing them is better that having in the code and unknown to us.

The Exiv2 development plan is to create a new branch called 'main' and to release Exiv2 v1.00 from that branch on 2021-12-15.  We would like to ask you to focus your attention on 'main'.  We will fix the issues you have opened on 0.27-maintenance and ship that as v0.27.4 on/before 2021-05-22.  If we ever make another release from the 0.27-maintenance branch, we will back-port security fixes from 'main'.  

I appreciate the effort that you and your co-workers are putting into the important matter of security.  I apologise for saying 'bombardment'.  My hope this week was to finish my 13 years of working on Exiv2.  I was distressed to see those CVEs arrive on day on which I intended to retire!

---

Comment by kevinbackhouse on 2021-04-09 09:17:14+00:00:
I am unable to reproduce this. I tested it on Ubuntu 20.04, using the latest version of 0-27-maintenance (commit [05ec05342e17dc94670db1818447c06d0da8f41a](https://github.com/Exiv2/exiv2/commit/05ec05342e17dc94670db1818447c06d0da8f41a)). These are the exact steps that I tried:

```
git checkout 0.27-maintenance 
mkdir build
cd build
CC=clang CXX=clang++ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS="-fsanitize=address" -DCMAKE_C_FLAGS="-fsanitize=address" -DCMAKE_EXE_LINKER_FLAGS="-fsanitize=address" -DCMAKE_MODULE_LINKER_FLAGS="-fsanitize=address"
make -j8
./bin/exiv2 ~/Downloads/tests_1bd0a5f4935b053f33ac00f931dde1f47a043487
```

I do not see any ASAN failures.

---

Comment by kevinbackhouse on 2021-04-09 09:24:52+00:00:
Oh, I see. I missed the `in` parameter.

---

Comment by clanmills on 2021-04-09 09:37:25+00:00:
@kevinbackhouse I also missed that on https://github.com/Exiv2/exiv2/issues/1529#issuecomment-815513754

I reproduced #1529 as follows:

```bash
.../foo $ ls -l
total 88
-rw-r--r--@ 1 rmills  staff  40609  8 Apr 08:01 tests_83a94b3337206caa6803f625eb63db061395cf14
-rw-r--r--@ 1 rmills  staff      9  8 Apr 08:09 tests_83a94b3337206caa6803f625eb63db061395cf14.exv
.../foo $ exiv2 in tests_83a94b3337206caa6803f625eb63db061395cf14
=================================================================
==52084==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000001b7 at pc 0x00010525f7f4 bp 0x7ffeeab2ed10 sp 0x7ffeeab2ed08
WRITE of size 8 at 0x6020000001b7 thread T0
    #0 0x10525f7f3 in Exiv2::Jp2Image::doWriteMetadata(Exiv2::BasicIo&)+0x2143 (libexiv2.0.27.4.2.dylib:x86_64+0xf27f3)
```
I believe similar medicine is needed for this issue.

The 'in' command is 'insert'.  It reads metadata from tests_xxxxx.exv and updates tests_xxxxx. 

---

Comment by kevinbackhouse on 2021-04-09 09:52:49+00:00:
@clanmills: when I have fuzzed exiv2 in the past, I did not try any of these extra command line options. So my testing probably didn't hit any of these "encode" methods. So it seems quite plausible that there are several more of these bugs lurking there.

From a security perspective, these command line arguments seem much less interesting to me than vanilla exiv2. I can imagine somebody downloading a untrusted image off the internet and using exiv2 to look at it's metadata. I have a much harder time imagining somebody downloading a pair of untrusted files like this and running `exiv2 in ...` on them.

---

Comment by pydera on 2021-04-09 09:56:24+00:00:
I would still suggest to change the type of 'size' from uint32_t to size_t.

---

Comment by clanmills on 2021-04-09 10:10:22+00:00:
As always, Kevin, you are saying smart things.  I also missed the unusual/obscure 'in' command. 

You will be aware that I was in a state of distress yesterday about those CVEs.  However, I've had a nice conversation with @henices.  The security folks in China are on our side.  Their work will make Exiv2 stronger.

My brain isn't up to thinking about the merits of size_t and uint32_t.  I believe the CRW format is 32 bit, so either will work.  I would change the one that minimises casts.

---

Comment by kevinbackhouse on 2021-04-09 10:23:50+00:00:
@pydera: I think `uint32_t` is a better choice than `size_t` for this code. The reason is that the type of [`CiffComponent::size()`](https://github.com/Exiv2/exiv2/blob/0230620e6ea5e2da0911318e07ce6e66d1ebdf22/src/crwimage_int.hpp#L225) is `uint32_t`, which in turn is because we are [parsing](https://github.com/Exiv2/exiv2/blob/05ec05342e17dc94670db1818447c06d0da8f41a/src/crwimage_int.cpp#L266) a `uint32_t` from the input file. Introducing `size_t` here would just add a risk of the code behaving differently on a 64 bit platform compared to 32 bit, for no good reason.

---

Comment by pydera on 2021-04-09 10:25:29+00:00:
@kevinbackhouse as far as I can see DataBuf.size() returns 'long' (int64_t on LP64). I did not look deeper into this, but was afraid that it might be possible to handcraft files where a size of >uint32_t-max could be returned and then overflow the uint32_t size.

---

Comment by kevinbackhouse on 2021-04-09 10:42:22+00:00:
@pydera: Yes, I agree that `size_t` would probably be a better choice for DataBuf.size(), rather than `long`. Unfortunately `long` is very widely used in this codebase so I think it would be quite a lot of work to switch everything over to `size_t`. My biggest concern is that `long` is 32 bits on Windows, so there is a higher risk of an integer overflow on Windows. The good news is that the `new` in `DataBuf`'s [constructor](https://github.com/Exiv2/exiv2/blob/05ec05342e17dc94670db1818447c06d0da8f41a/src/types.cpp#L135) would throw an exception (and terminate the program) if you managed to overflow the size.

---

Comment by pydera on 2021-04-09 10:54:07+00:00:
@kevinbackhouse Agreed. My point of view was "just looking at THIS function" without deeper research on CiffComponent, I saw that DataBuf::size() could potentially overflow 'size'. Not looking at CiffComponent I concluded that changing 'size' to size_t would always be a safe choice while uint32_t needs further investigation.
As CiffComponent::size() returns indeed uint32_t we are safe here, but looking at 'this' you could only know by also looking at CiffComponent, whereas size_t would make it clear at first sight.


---

Comment by henices on 2021-04-09 11:21:34+00:00:
> @clanmills: when I have fuzzed exiv2 in the past, I did not try any of these extra command line options. So my testing probably didn't hit any of these "encode" methods. So it seems quite plausible that there are several more of these bugs lurking there.
> 
> From a security perspective, these command line arguments seem much less interesting to me than vanilla exiv2. I can imagine somebody downloading a untrusted image off the internet and using exiv2 to look at it's metadata. I have a much harder time imagining somebody downloading a pair of untrusted files like this and running `exiv2 in ...` on them.

there is another way to exploit these bugs, a single image file is enough.

---

Comment by fgeek on 2021-08-06 08:37:06+00:00:
[CVE-2021-31292](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-31292) has been assigned for this issue.

Commit References:
0230620e6ea5e2da0911318e07ce6e66d1ebdf22
83a94b3337206caa6803f625eb63db061395cf14
05ec05342e17dc94670db1818447c06d0da8f41a
1bd0a5f4935b053f33ac00f931dde1f47a043487

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
mkdir -p build
cd build
cmake -DEXIV2_ENABLE_PNG=ON -DEXIV2_ENABLE_WEBREADY=ON -DEXIV2_ENABLE_CURL=OFF -DEXIV2_ENABLE_BMFF=ON -DEXIV2_TEAM_WARNINGS_AS_ERRORS=ON -DBUILD_SHARED_LIBS=OFF -DCMAKE_CXX_COMPILER="${CXX}" -DCMAKE_CXX_FLAGS="${CXXFLAGS}" -DEXIV2_BUILD_FUZZ_TESTS=ON -DEXIV2_TEAM_OSS_FUZZ=ON -DLIB_FUZZING_ENGINE="${LIB_FUZZING_ENGINE}" ..
make -j $(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y cmake make ccache python3 libexpat1-dev zlib1g-dev libssh-dev libcurl4-openssl-dev libxml2-utils
RUN git clone https://github.com/exiv2/exiv2 exiv2
RUN git -C exiv2 checkout 9ac01692487e1fd5e5240ea95bf9c43bf281775a
WORKDIR $SRC/exiv2
COPY build.sh $SRC/

Additional files: