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

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/sleuthkit/sleuthkit/issues/1266
## Description:
Issue: sleuthkit/sleuthkit#1266
Title: AddressSanitizer: out-of-bounds unmapped memory access in ntfs_fix_idxrec (tsk/fs/ntfs_dent.cpp:680:16)
State: open
Created by: glen-mac
Created at: 2018-06-03 13:32:43+00:00

Issue Body:
Hey there, I have discovered an unmapped memory access in the sleuth kit at: [ntfs_dent.cpp:680:16](https://github.com/sleuthkit/sleuthkit/blob/4efa611df63e627f7850b10b8f7eda36787dbc94/tsk/fs/ntfs_dent.cpp#L680)

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/sleuthkit-4.0.1) up until and including the latest release [4.6.1](https://github.com/sleuthkit/sleuthkit/tree/sleuthkit-4.6.1)

You can find a POC that triggers the bug [here](https://github.com/sleuthkit/sleuthkit/files/2065655/ntfsdent_crashes.zip).

The full ASAN report is shown below:
```
↳ tools/fstools/fls -lrp crash.file
=================================================================
==7493==ERROR: AddressSanitizer: SEGV on unknown address 0x621000024d28 (pc 0x000000691ae9 bp 0x7ffd4abaa8b0 sp 0x7
ffd4abaa7e0 T0)
    #0 0x691ae8 in ntfs_fix_idxrec(NTFS_INFO*, ntfs_idxrec*, unsigned int) /home/glenn/temp/sleuthkit/tsk/fs/ntfs_d
ent.cpp:680:16
    #1 0x680008 in ntfs_dir_open_meta /home/glenn/temp/sleuthkit/tsk/fs/ntfs_dent.cpp:1167:17
    #2 0x5019cf in tsk_fs_dir_open_meta /home/glenn/temp/sleuthkit/tsk/fs/fs_dir.c:290:14
    #3 0x503a02 in tsk_fs_dir_walk_lcl /home/glenn/temp/sleuthkit/tsk/fs/fs_dir.c:556:19
    #4 0x50375c in tsk_fs_dir_walk /home/glenn/temp/sleuthkit/tsk/fs/fs_dir.c:817:14
    #5 0x4fb072 in tsk_fs_fls /home/glenn/temp/sleuthkit/tsk/fs/fls_lib.c:262:12
    #6 0x4efba8 in main /home/glenn/temp/sleuthkit/tools/fstools/fls.cpp:307:9
    #7 0x7f91cec5e82f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291
    #8 0x41a008 in _start (/home/glenn/temp/sleuthkit/results-binaries/fls-sleuthkit-4.6.1+0x41a008)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/glenn/temp/sleuthkit/tsk/fs/ntfs_dent.cpp:680:16 in ntfs_fix_idxrec(NTFS_INFO
*, ntfs_idxrec*, unsigned int)
==7493==ABORTING
```


Comments:
Comment by FIOpwK on 2019-07-08 05:44:30+00:00:
CVE-2018-11737 was assigned to this issue (not requested by me)

Attachments:
https://github.com/sleuthkit/sleuthkit/files/2065655/ntfsdent_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;
}