hugsy / gef

GEF (GDB Enhanced Features) - a modern experience for GDB with advanced debugging capabilities for exploit devs & reverse engineers on Linux
https://hugsy.github.io/gef
MIT License
7.01k stars 737 forks source link

heap arenas NoneType + heap-view doesn’t find heap #854

Closed 0xricksanchez closed 2 years ago

0xricksanchez commented 2 years ago

Step 1: Describe your environment

Step 2: Describe your problem

Steps to reproduce

  1. gdb -q ./a.out
  2. start
  3. break *puts
  4. c
  5. Check heap

Minimalist test case

// test.c
// clang test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv) {
  void *p = malloc(0x64);
  void *r = malloc(0x28);
  void *q = malloc(0x64);

  puts("Just some filler content to break on");
  fflush(stdout);

  return 0;
}

Observed Results

gef➤  heap chunks
Chunk(addr=0x555555559010, size=0x290, flags=PREV_INUSE)
    [0x0000555555559010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x5555555592a0, size=0x70, flags=PREV_INUSE)
    [0x00005555555592a0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x555555559310, size=0x30, flags=PREV_INUSE)
    [0x0000555555559310     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x555555559340, size=0x70, flags=PREV_INUSE)
    [0x0000555555559340     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x5555555593b0, size=0x410, flags=PREV_INUSE)
    [0x00005555555593b0     4a 75 73 74 20 73 6f 6d 65 20 66 69 6c 6c 65 72    Just some filler]
Chunk(addr=0x5555555597c0, size=0x20850, flags=PREV_INUSE)
    [0x00005555555597c0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
gef➤  heap arenas
Arena(base=0x7ffff7f8a400, top=0x0, last_remainder=0x0, next=0x0, next_free=0x0, system_mem=0x0)
[!] Command 'heap arenas' failed to execute properly, reason: 'NoneType' object has no attribute 'cast'
gef➤  heap-view
[!] The heap has not been initialized
gef➤  vis
[!] The heap has not been initialized
gef➤  

What happened? This could be a description, log output, etc.

Expected results

Traces

image

hugsy commented 2 years ago

I cannot reproduce your errors, followed step by step but got everything working (except an unrelated type bug in heap-view, but the rest is fine). Tried on Debian 12 & Ubuntu 20.04.

image

Can you try to reproduce your bug on another system and/or libc version?

0xricksanchez commented 2 years ago

Interesting! Just booted a fresh debian bullseye docker container and installed gef. I do not have the arena.top being 0x0 problem either. Since the heap-view fix has not been merged at the time of testing I'm running into an expected error:

gef➤  heap-view

─────────────────────────────── Exception raised ───────────────────────────────
TypeError: 'tuple' object is not callable
───────────────────────────── Detailed stacktrace ──────────────────────────────
↳ File "/home/dbg/.gef-extras/scripts/visualize_heap.py", line 30, in collect_known_values()
    →     if gef.libc.version() >= (2, 27):
↳ File "/home/dbg/.gef-extras/scripts/visualize_heap.py", line 121, in do_invoke()
    →     known_values = collect_known_values()
↳ File "~/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py", line 366, in wrapper()
    →     _cmdline_ = "ida-rpyc comments del"
↳ File "~/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py", line 255, in wrapper()
    →     _example_ = "{:s}".format(_cmdline_)
↳ File "~/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py", line 4446, in invoke()
    →                 bufferize(self.do_invoke)(argv)

Which I can confirm you have fixed in https://github.com/hugsy/gef-extras/pull/71 (at least the visualization works inside my Debian container using that PR branch)

Now I wonder if this is a Manjaro issue or if my system is borked. I'll later set up a Manjaro container and test again.

Viz bug?

However, the visualization behavior looks strange:

image

### heap chunk [addr] bug? NVM.

0xricksanchez commented 2 years ago

When running both Manjaro and Arch Linux docker containers I'm running into the same problem I have filed an issue here for:

gef➤  shell cat /etc/os-release
NAME="Arch Linux"
PRETTY_NAME="Arch Linux"
ID=arch
BUILD_ID=rolling
VERSION_ID=TEMPLATE_VERSION_ID
ANSI_COLOR="38;2;23;147;209"
HOME_URL="https://archlinux.org/"
DOCUMENTATION_URL="https://wiki.archlinux.org/"
SUPPORT_URL="https://bbs.archlinux.org/"
BUG_REPORT_URL="https://bugs.archlinux.org/"
LOGO=archlinux-logo

gef➤  heap chunks
Chunk(addr=0x56285dbdc010, size=0x290, flags=PREV_INUSE)
    [0x000056285dbdc010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x56285dbdc2a0, size=0x70, flags=PREV_INUSE)
    [0x000056285dbdc2a0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x56285dbdc310, size=0x30, flags=PREV_INUSE)
    [0x000056285dbdc310     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x56285dbdc340, size=0x70, flags=PREV_INUSE)
    [0x000056285dbdc340     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x56285dbdc3b0, size=0x20c60, flags=PREV_INUSE)
    [0x000056285dbdc3b0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]

gef➤  heap arenas
Arena(base=0x7f6b0eaa7400, top=0x0, last_remainder=0x0, next=0x0, next_free=0x0, system_mem=0x0)
[!] Command 'heap arenas' failed to execute properly, reason: 'NoneType' object has no attribute 'cast'

gef➤  vis
[!] The heap has not been initialized

For good measure I set up yet another test system EndeavourOS_Artemis_22_6 as a fully fledged VM. It shows the same behavior... :( Not sure what the best course of action here is

// Edit:

My Manjaro host as well as the EndeavourOS VM both have GLIBC 2.35.

hugsy commented 2 years ago

Not the latest version of Arch but still no problem:

image

hugsy commented 2 years ago

Note that I did the test with the heap-view version in the fix_heap_viz_new_api branch

0xricksanchez commented 2 years ago

Assumption: This all may be related to different GLIBC versions as my testing on Arch/Manjaro has been exclusively done with GLIBC 2.35, where I ran into initial issue. Other tests with GLIBC 2.31 and 2.33 above showed a sane behavior.

Backtracking

Testing on GLIBC 2.34 on an ubuntu system with the following setup:

# GLIBC 2.34
# docker build -f <this_dockerfile> -t gef_glibc234 .
# docker run -it --rm gef_glibc234 /bin/bash
FROM ubuntu:impish
ENV DEBIAN_FRONTEND noninteractive

RUN apt update && apt install -y file git binutils python3 python3-pip vim gcc gdb curl wget python-is-python3 locales && \
    sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
    dpkg-reconfigure --frontend=noninteractive locales && \
    update-locale LANG=en_US.UTF-8 && \
    bash -c "$(curl -fsSL https://gef.blah.cat/sh)" && \
    wget https://github.com/hugsy/gef/raw/main/scripts/gef-extras.sh && \
    sed -i -e "s/git clone/git clone -b 'fix_heap_viz_new_api'/g" gef-extras.sh && \
    chmod 755 gef-extras.sh && \
    ./gef-extras.sh && \
    apt-get clean

ENV LC_ALL=en_US.UTF-8

Yields the following: image

arena.top is not 0x0 but the vis/heap-view command runs into another issue, while the top chunk is again listed twice in the heap chunks view

hugsy commented 2 years ago

I can repro the bug with the Dockerfile you provided 🎉

0xricksanchez commented 2 years ago

Observation

https://github.com/hugsy/gef-extras/pull/71 does not fix it for me

Test

Built a fresh Ubuntu impish container (as posted above) and the same test case as before, with the only change of getting rid of the sed as the gef-extras branch fix_heap_viz_new_api does not exist anymore.

gef➤  vis
[!] Command 'visualize-libc-heap-chunks' failed to execute properly, reason: Cannot access memory at address 0x55df2592bff0
gef➤  gef config gef.debug 1
gef➤  heap chunks
Chunk(addr=0x55df2592c010, size=0x290, flags=PREV_INUSE)
    [0x000055df2592c010     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x55df2592c2a0, size=0x70, flags=PREV_INUSE)
    [0x000055df2592c2a0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x55df2592c310, size=0x30, flags=PREV_INUSE)
    [0x000055df2592c310     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x55df2592c340, size=0x70, flags=PREV_INUSE)
    [0x000055df2592c340     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x55df2592c3b0, size=0x20c60, flags=PREV_INUSE)
    [0x000055df2592c3b0     00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................]
Chunk(addr=0x55df2592c3b0, size=0x20c60, flags=PREV_INUSE)  ←  top chunk
gef➤  vis

─────────────────────────────── Exception raised ───────────────────────────────
MemoryError: Cannot access memory at address 0x55df2592bff0
───────────────────────────── Detailed stacktrace ──────────────────────────────
↳ File "/root/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py", line 10149, in read()
    →     return gdb.selected_inferior().read_memory(addr, length).tobytes()
↳ File "/root/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py", line 10154, in read_integer()
    →     mem = self.read(addr, sz)
↳ File "/root/.gef-extras/scripts/visualize_heap.py", line 172, in do_invoke()
    →     value = gef.memory.read_integer(addr)
↳ File "/root/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py", line 366, in wrapper()
    →     return f(*args, **kwargs)
↳ File "/root/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py", line 255, in wrapper()
    →     rv = f(*args, **kwargs)
↳ File "/root/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py", line 4446, in invoke()
    →     bufferize(self.do_invoke)(argv)
─────────────────────────────────── Version ────────────────────────────────────
GEF: (Standalone)
Blob Hash(/root/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py): 8dc57b700e3c1c85822449033a01c94dfae9e4a6
SHA256(/root/.gef-2b72f5d0d9f0f218a91cd1ca5148e45923b950d5.py): 63d3e10d38a367c3e4d37de8e0701bcdff2a4e7c9a0a4ec5d83ccb8b2fe6188d
GDB: 11.1
GDB-Python: 3.9
obsolete loaded_command_names
Loaded commands: $, aliases, aliases add, aliases ls, aliases rm, aslr, canary, checksec, context, dereference, edit-flags, elf-info, entry-break, format-string-helper, functions, gef-remote, got, heap, heap arenas, heap bins, heap bins fast, heap bins large, heap bins small, heap bins tcache, heap bins unsorted, heap chunk, heap chunks, heap set-arena, heap-analysis-helper, hexdump, hexdump byte, hexdump dword, hexdump qword, hexdump word, highlight, highlight add, highlight clear, highlight list, highlight remove, hijack-fd, is-syscall, ksymaddr, memory, memory list, memory reset, memory unwatch, memory watch, name-break, nop, patch, patch byte, patch dword, patch qword, patch string, patch word, pattern, pattern create, pattern search, pcustom, pcustom edit, pcustom list, pcustom show, pie, pie attach, pie breakpoint, pie delete, pie info, pie remote, pie run, print-format, process-search, process-status, registers, reset-cache, scan, search-pattern, shellcode, shellcode get, shellcode search, stub, syscall-args, theme, trace-run, version, vmmap, xfiles, xinfo, xor-memory, xor-memory display, xor-memory patch, assemble, bincompare, bytearray, current-stack-frame, error, exploit-template, ftrace, g, hh, ida-rpyc, ida-rpyc breakpoints, ida-rpyc breakpoints list, ida-rpyc comments, ida-rpyc comments add, ida-rpyc comments del, ida-rpyc highlight, ida-rpyc highlight add, ida-rpyc highlight del, ida-rpyc info, ida-rpyc jump, pc, peek-pointers, pt, ptc, r, retdec, ropper, rpyc-remote, set-permission, sxe, tc, tt, u, unicorn-emulate, vereference, visualize-libc-heap-chunks, xref-telescope, xs
───────────────────────────── Last 10 GDB commands ─────────────────────────────
    1  start
    2  b *puts
    3  c
    4  vis
    5  gef config gef.debug 1
    6  heap chunks
    7  vis
───────────────────────────── Runtime environment ──────────────────────────────
* GDB: 11.1
* Python: 3.9.7 - final
* OS: Linux - 5.15.49-1-MANJARO (x86_64)
lsb_release is missing, cannot collect additional debug information
────────────────────────────────────────────────────────────────────────────────

gef➤  !cat /etc/os-release 
PRETTY_NAME="Ubuntu 21.10"
NAME="Ubuntu"
VERSION_ID="21.10"
VERSION="21.10 (Impish Indri)"
VERSION_CODENAME=impish
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=impish

gef➤  pi get_libc_version()
(2, 34)
hugsy commented 2 years ago

Re-opening, it was auto-closed "by accident" after merging https://github.com/hugsy/gef-extras/pull/71 The bug is not fixed.

hugsy commented 2 years ago

So, your problem is two-fold:

Feel free to test on your side and let me know.

Cheers,

0xricksanchez commented 2 years ago

Just re-testing on my Debian GLIBC 2.34 container first with the following changes made to the original Dockerfile above:

// snip
    wget https://raw.githubusercontent.com/hugsy/gef/heap_bugz/gef.py && \
    echo "source /root/gef.py" > /root/.gdbinit && \
    wget https://github.com/hugsy/gef/raw/main/scripts/gef-extras.sh && \
    sed -i -e "s/git clone/git clone -b 'fix_visualize_heap'/g" gef-extras.sh && \
// snip

Same test scenario as before:

image

This looks excellent!

However, I noticed some weird behavior with heap bins:

  1. This spits out far too many chunks with insane sizes that actually always are an address within the mapped RW section of the libc, and
  2. Paired with tons of deprecation warnings makes this output quite unusable rn

Here's a snippet as it's not even close to fitting on a single screen size:

image image

Using the same dummy 3-malloc test binary from above I doubt that there should be:

[+] Found 62 chunks in 62 small non-empty bins.
[+] Found 63 chunks in 63 large non-empty bins.
hugsy commented 2 years ago

Yes, CI on Ubuntu22.04 reported this. It's probably just a bug from the new API I'm putting in place. I will look it up

0xricksanchez commented 2 years ago

@hugsy I can confirm it works fine on the Debian GLIBC 2.34 container now with the heap_bugz branch:

image


BUT it completely breaks on this Arch + GLIBC 2.35 container:

FROM docker.io/archlinux
ENV DEBIAN_FRONTEND noninteractive

WORKDIR /root/
RUN pacman -Syu --noconfirm && \
    echo "y" | pacman -S --noconfirm base-devel git binutils python3 python-pip neovim gcc gdb curl wget && \
    sed -i -e 's/#en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && \
    locale-gen && \
    wget https://raw.githubusercontent.com/hugsy/gef/heap_bugz/gef.py && \
    echo "source /root/gef.py" > /root/.gdbinit && \
    wget https://github.com/hugsy/gef/raw/main/scripts/gef-extras.sh && \
    sed -i -e "s/git clone/git clone -b 'fix_visualize_heap'/g" gef-extras.sh && \
    chmod 755 gef-extras.sh && \
    ./gef-extras.sh

ENV LC_ALL=en_US.UTF-8

With the same test binary:

image

hugsy commented 2 years ago

Must be an arch specific problem, ubuntu 22.04 with libc 2.35 works fine.

image

0xricksanchez commented 2 years ago

100% can confirm this.

hugsy commented 2 years ago

Arch is doing some funny biz here: image

hugsy commented 2 years ago

Those bugs were confirmed to be fixed by #869 and hugsy/gef-extras#77 on Debian, Ubuntu & Fedora.

Only ArchLinux seems to fail for some reason, nonetheless we can close the issue. @0xricksanchez Feel free to open one if you get more insight as to why this fix won't work on Arch but some more help will be needed there.