Project: sleuthkit (sleuthkit/sleuthkit), language: c++
Vulnerability instance: sleuthkit.cve-2018-11740
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/sleuthkit/sleuthkit/issues/1264
## Description:
Issue: sleuthkit/sleuthkit#1264
Title: AddressSanitizer: user-after-free (UAF) and out-of-bounds read (OOB) in tsk_UTF16toUTF8 (tsk/base/tsk_unicode.c:159:14)
State: open
Created by: glen-mac
Created at: 2018-06-03 03:45:46+00:00

Issue Body:
Hey there, I have discovered a one byte out-of-bounds read in the sleuth kit at: [tsk_unicode.c:159:14](https://github.com/sleuthkit/sleuthkit/blob/4efa611df63e627f7850b10b8f7eda36787dbc94/tsk/base/tsk_unicode.c#L159)

Found when fuzzing commit 4efa611df63e627f7850b10b8f7eda36787dbc94.

Compile flags to reproduce: 
```
CC=clang CXX=clang++ CFLAGS='-fsanitize=address -g -O2 -fno-omit-frame-pointer' CXXFLAGS=$CFLAGS make
```

System information:
```
$ uname -a
Linux s127422 3.13.0-137-generic #186-Ubuntu SMP Mon Dec 4 19:09:19 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
```

This bug was found to be in sleuth kit releases from [4.0.1](https://github.com/sleuthkit/sleuthkit/tree/4.0.1) until the recent commit fuzzed 4efa611df63e627f7850b10b8f7eda36787dbc94.

You can find a collection of PoC files that trigger the bug [here](https://github.com/sleuthkit/sleuthkit/files/2065620/tskunicode_crashes.zip).

The full ASAN report is shown below:
```
↳ tools/fstools/fls -lrp crash.file
=================================================================
==3797==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x619000019577 at pc 0x0000006d754e bp 0x7ffe2a703
1e0 sp 0x7ffe2a7031d8
READ of size 1 at 0x619000019577 thread T0
    #0 0x6d754d in tsk_UTF16toUTF8 /home/glenn/temp/sleuthkit/tsk/base/tsk_unicode.c:159:14
    #1 0x635502 in ntfs_proc_attrseq /home/glenn/temp/sleuthkit/tsk/fs/ntfs.c:1723:17
    #2 0x62ebb1 in ntfs_dinode_copy /home/glenn/temp/sleuthkit/tsk/fs/ntfs.c:2626:19
    #3 0x5fc4e4 in ntfs_inode_walk /home/glenn/temp/sleuthkit/tsk/fs/ntfs.c:3937:17
    #4 0x67455b in ntfs_dir_open_meta /home/glenn/temp/sleuthkit/tsk/fs/ntfs_dent.cpp:1228:13
    #5 0x50110f in tsk_fs_dir_open_meta /home/glenn/temp/sleuthkit/tsk/fs/fs_dir.c:276:14
    #6 0x503942 in tsk_fs_dir_walk_lcl /home/glenn/temp/sleuthkit/tsk/fs/fs_dir.c:551:19
    #7 0x50369c in tsk_fs_dir_walk /home/glenn/temp/sleuthkit/tsk/fs/fs_dir.c:803:14
    #8 0x4faa72 in tsk_fs_fls /home/glenn/temp/sleuthkit/tsk/fs/fls_lib.c:262:12
    #9 0x4efaa0 in main /home/glenn/temp/sleuthkit/tools/fstools/fls.cpp:303:9
    #10 0x7fee8bfa382f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291
    #11 0x419f38 in _start (/home/glenn/temp/sleuthkit/tools/fstools/fls+0x419f38)

0x619000019577 is located 247 bytes to the right of 1024-byte region [0x619000019080,0x619000019480)
allocated by thread T0 here:
    #0 0x4ba068 in __interceptor_malloc (/home/glenn/temp/sleuthkit/tools/fstools/fls+0x4ba068)
    #1 0x6d05b1 in tsk_malloc /home/glenn/temp/sleuthkit/tsk/base/mymalloc.c:32:16
    #2 0x5fc07a in ntfs_inode_walk /home/glenn/temp/sleuthkit/tsk/fs/ntfs.c:3879:29
    #3 0x67455b in ntfs_dir_open_meta /home/glenn/temp/sleuthkit/tsk/fs/ntfs_dent.cpp:1228:13
    #4 0x50110f in tsk_fs_dir_open_meta /home/glenn/temp/sleuthkit/tsk/fs/fs_dir.c:276:14
    #5 0x503942 in tsk_fs_dir_walk_lcl /home/glenn/temp/sleuthkit/tsk/fs/fs_dir.c:551:19
    #6 0x50369c in tsk_fs_dir_walk /home/glenn/temp/sleuthkit/tsk/fs/fs_dir.c:803:14
    #7 0x4faa72 in tsk_fs_fls /home/glenn/temp/sleuthkit/tsk/fs/fls_lib.c:262:12
    #8 0x4efaa0 in main /home/glenn/temp/sleuthkit/tools/fstools/fls.cpp:303:9
    #9 0x7fee8bfa382f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291

SUMMARY: AddressSanitizer: heap-buffer-overflow /home/glenn/temp/sleuthkit/tsk/base/tsk_unicode.c:159:14 in tsk_UTF16toUTF8
Shadow bytes around the buggy address:
  0x0c327fffb250: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c327fffb260: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c327fffb270: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c327fffb280: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c327fffb290: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c327fffb2a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa[fa]fa
  0x0c327fffb2b0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fffb2c0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fffb2d0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fffb2e0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
  0x0c327fffb2f0: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
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
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  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
==3797==ABORTING
```


Comments:
Comment by FIOpwK on 2019-07-08 05:41:36+00:00:
CVE-2018-11740 was assigned for this issue. (not requested by me)

Attachments:
https://github.com/sleuthkit/sleuthkit/files/2065620/tskunicode_crashes.zip

Commit References:
4efa611df63e627f7850b10b8f7eda36787dbc94

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
export CFLAGS="$CFLAGS -Wno-error=non-c-typedef-for-linkage"
export CXXFLAGS="$CXXFLAGS -Wno-error=non-c-typedef-for-linkage"
./bootstrap
./configure --enable-static --disable-shared --disable-java
make -j$(nproc)

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool
RUN git clone https://github.com/sleuthkit/sleuthkit sleuthkit
RUN git -C sleuthkit checkout 9b776576aa6a1fbf328e82fd7cb6b428179de016
WORKDIR $SRC/sleuthkit
COPY build.sh $SRC/

Additional files:
filename: sleuthkit_fls_fuzzer.cc
content: // Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stddef.h>
#include <stdint.h>

#include "sleuthkit/tsk/tsk_tools_i.h"
#include "sleuthkit_mem_img.h"

#ifndef FSTYPE
#error Define FSTYPE as a valid value of TSK_FS_TYPE_ENUM.
#endif

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  TSK_IMG_INFO *img;
  TSK_FS_INFO *fs;

  img = mem_open(data, size);
  if (img == nullptr)
    return 0;

  fs = tsk_fs_open_img(img, 0, FSTYPE);
  if (fs != nullptr) {
    tsk_fs_fls(fs, TSK_FS_FLS_FULL, fs->root_inum, TSK_FS_DIR_WALK_FLAG_RECURSE,
               nullptr, 0);

    fs->close(fs);
  }

  img->close(img);
  return 0;
}

filename: sleuthkit_mem_img.h
content: // Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef MEM_IMG_H
#define MEM_IMG_H

#include <stddef.h>
#include <stdint.h>

#include "sleuthkit/tsk/tsk_tools_i.h"

typedef struct {
  TSK_IMG_INFO img_info;
  const uint8_t *data;
  size_t size;
} IMG_MEM_INFO;

static ssize_t mem_read(TSK_IMG_INFO *img_info, TSK_OFF_T offset, char *buf,
                        size_t len) {
  IMG_MEM_INFO *mem_info = reinterpret_cast<IMG_MEM_INFO *>(img_info);
  // Bounds-checking exists in the real drivers.
  if (offset > mem_info->size) {
    return -1;
  }
  ssize_t read_len = len;
  if (offset + len > mem_info->size) {
    read_len = mem_info->size - offset;
  }
  if (memcpy(buf, mem_info->data + offset, read_len) == nullptr) {
    return -1;
  } else {
    return read_len;
  }
}

static void mem_close(TSK_IMG_INFO *img_info) {
  IMG_MEM_INFO *mem_info = reinterpret_cast<IMG_MEM_INFO *>(img_info);
  tsk_deinit_lock(&(img_info->cache_lock));
  free(mem_info);
}

static void mem_imgstat(TSK_IMG_INFO *img_info, FILE *hFile) {}

TSK_IMG_INFO *mem_open(const uint8_t *data, size_t size) {
  IMG_MEM_INFO *inmemory_img =
      reinterpret_cast<IMG_MEM_INFO *>(malloc(sizeof(IMG_MEM_INFO)));
  TSK_IMG_INFO *img;
  if (inmemory_img == nullptr) {
    return nullptr;
  }
  img = reinterpret_cast<TSK_IMG_INFO *>(inmemory_img);
  img->itype = TSK_IMG_TYPE_RAW;
  img->read = mem_read;
  img->close = mem_close;
  img->imgstat = mem_imgstat;
  img->size = size;
  img->sector_size = 512;
  tsk_init_lock(&(img->cache_lock));
  inmemory_img->data = data;
  inmemory_img->size = size;
  return img;
}

#endif // # MEM_IMG_H

filename: sleuthkit_mmls_fuzzer.cc
content: // Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <stddef.h>
#include <stdint.h>

#include "sleuthkit/tsk/tsk_tools_i.h"
#include "sleuthkit_mem_img.h"

#ifndef VSTYPE
#error Define VSTYPE as a valid value of TSK_VS_TYPE_ENUM.
#endif

static TSK_WALK_RET_ENUM part_act(TSK_VS_INFO *vs, const TSK_VS_PART_INFO *part,
                                  void *ptr) {
  return TSK_WALK_CONT;
}

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  TSK_IMG_INFO *img;
  TSK_VS_INFO *vs;

  img = mem_open(data, size);
  if (img == nullptr)
    return 0;

  vs = tsk_vs_open(img, 0, VSTYPE);
  if (vs != nullptr) {
    tsk_vs_part_walk(vs, 0, vs->part_count - 1, TSK_VS_PART_FLAG_ALL, part_act,
                     nullptr);

    tsk_vs_close(vs);
  }

  img->close(img);
  return 0;
}