From 8031fda891522460af1d12b0e2e9968459ce08de Mon Sep 17 00:00:00 2001 From: ValekoZ Date: Tue, 23 Apr 2024 03:14:26 +0200 Subject: [PATCH] Fix problems due to multiple matches in filename filters (#1083) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Expected behavior - Find the `main_arena` even if there is "libc" in the path of files that aren't libc. - Warn the user when there are multiple matches when using `$_base("xxx")` - Search for "xxx" in the filename instead of the entire path when possible when using `$_base("xxx")` ## Current behavior ``` (remote) gef➤ vmmap [ Legend: Code | Heap | Stack ] Start End Offset Perm Path 0x000061077d582000 0x000061077d583000 0x0000000000001000 r-- /home/user/ctf-fcsc/file-checker-src/public/file-checker_remotelibc 0x000061077d583000 0x000061077d584000 0x0000000000001000 r-x /home/user/ctf-fcsc/file-checker-src/public/file-checker_remotelibc 0x000061077d584000 0x000061077d585000 0x0000000000001000 r-- /home/user/ctf-fcsc/file-checker-src/public/file-checker_remotelibc 0x000061077d585000 0x000061077d586000 0x0000000000001000 r-- /home/user/ctf-fcsc/file-checker-src/public/file-checker_remotelibc 0x000061077d586000 0x000061077d587000 0x0000000000001000 rw- /home/user/ctf-fcsc/file-checker-src/public/file-checker_remotelibc 0x000061077d587000 0x000061077d589000 0x0000000000002000 rw- /home/user/ctf-fcsc/file-checker-src/public/file-checker_remotelibc 0x0000715ebf600000 0x0000715ebf628000 0x0000000000028000 r-- /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/libc.so.6 0x0000715ebf628000 0x0000715ebf7b0000 0x0000000000188000 r-x /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/libc.so.6 0x0000715ebf7b0000 0x0000715ebf7ff000 0x000000000004f000 r-- /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/libc.so.6 0x0000715ebf7ff000 0x0000715ebf803000 0x0000000000004000 r-- /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/libc.so.6 0x0000715ebf803000 0x0000715ebf805000 0x0000000000002000 rw- /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/libc.so.6 0x0000715ebf805000 0x0000715ebf812000 0x000000000000d000 rw- 0x0000715ebf8fa000 0x0000715ebf8ff000 0x0000000000005000 rw- 0x0000715ebf8ff000 0x0000715ebf900000 0x0000000000001000 r-- /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/ld-linux-x86-64.so.2 0x0000715ebf900000 0x0000715ebf92b000 0x000000000002b000 r-x /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/ld-linux-x86-64.so.2 0x0000715ebf92b000 0x0000715ebf935000 0x000000000000a000 r-- /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/ld-linux-x86-64.so.2 0x0000715ebf935000 0x0000715ebf937000 0x0000000000002000 r-- /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/ld-linux-x86-64.so.2 0x0000715ebf937000 0x0000715ebf939000 0x0000000000002000 rw- /home/user/.cache/.pwntools-cache-3.11/libcdb_libs/8f2af70b7deed50338b9186c7dd60cef3826e18f/ld-linux-x86-64.so.2 0x00007fffda699000 0x00007fffda6ba000 0x0000000000021000 rw- [stack] 0x00007fffda711000 0x00007fffda715000 0x0000000000004000 r-- [vvar] 0x00007fffda715000 0x00007fffda717000 0x0000000000002000 r-x [vdso] (remote) gef➤ print $_base("libc") $1 = 0x61077d582000 (remote) gef➤ heap chunks [!] Invalid arena ``` --- gef.py | 42 +++++++++++++------ tests/api/misc.py | 12 ++++++ tests/binaries/Makefile | 3 ++ tests/binaries/collision.c | 16 +++++++ .../regressions/filename_collision_lookup.py | 27 ++++++++++++ 5 files changed, 88 insertions(+), 12 deletions(-) create mode 100644 tests/binaries/collision.c create mode 100644 tests/regressions/filename_collision_lookup.py diff --git a/gef.py b/gef.py index e78c20343..0abed7c1f 100644 --- a/gef.py +++ b/gef.py @@ -3529,11 +3529,28 @@ def process_lookup_path(name: str, perm: Permission = Permission.ALL) -> Optiona err("Process is not running") return None + matches: Dict[str, Section] = dict() for sect in gef.memory.maps: - if name in sect.path and sect.permission & perm: - return sect + filename = pathlib.Path(sect.path).name - return None + if name in filename and sect.permission & perm: + if sect.path not in matches.keys(): + matches[sect.path] = sect + + matches_count = len(matches) + + if matches_count == 0: + return None + + if matches_count > 1: + warn(f"{matches_count} matches! You should probably refine your search!") + + for x in matches.keys(): + warn(f"- '{x}'") + + warn("Returning the first match") + + return list(matches.values())[0] @lru_cache() @@ -10840,15 +10857,16 @@ def find_main_arena_addr() -> int: try: dbg("Trying to bruteforce main_arena address") # setup search_range for `main_arena` to `.data` of glibc - search_filter = lambda f: "libc" in f.filename and f.name == ".data" - dotdata = list(filter(search_filter, get_info_files()))[0] - search_range = range(dotdata.zone_start, dotdata.zone_end, alignment) - # find first possible candidate - for addr in search_range: - if GlibcArena.verify(addr): - dbg(f"Found candidate at {addr:#x}") - return addr - dbg("Bruteforce not successful") + search_filter = lambda f: "libc" in pathlib.Path(f.filename).name and f.name == ".data" + + for dotdata in list(filter(search_filter, get_info_files())): + search_range = range(dotdata.zone_start, dotdata.zone_end, alignment) + # find first possible candidate + for addr in search_range: + if GlibcArena.verify(addr): + dbg(f"Found candidate at {addr:#x}") + return addr + dbg("Bruteforce not successful") except Exception: pass diff --git a/tests/api/misc.py b/tests/api/misc.py index 5e47e2d4b..9f9a0f183 100644 --- a/tests/api/misc.py +++ b/tests/api/misc.py @@ -65,3 +65,15 @@ def test_func_show_last_exception(self): gdb.execute("gef config gef.propagate_debug_exception True") with pytest.raises(Exception): gdb.execute("hexdump byte *0") + + def test_func_process_lookup_path(self): + root, gdb = self._conn.root, self._gdb + gdb.execute("start") + + assert root.eval("process_lookup_path('meh')") is None + + libc = root.eval("process_lookup_path('libc')") + assert libc is not None + assert "libc" in pathlib.Path(libc.path).name + + assert root.eval("process_lookup_path('stack')") is not None diff --git a/tests/binaries/Makefile b/tests/binaries/Makefile index 0a8acc58f..594c12de8 100644 --- a/tests/binaries/Makefile +++ b/tests/binaries/Makefile @@ -27,6 +27,7 @@ all: $(LINKED) %.out : %.c @echo "[+] Building '$(TMPDIR)/$@'" + @mkdir -p $(TMPDIR) @$(CC) $(CFLAGS) $(EXTRA_FLAGS) -o $(TMPDIR)/$@ $? $(LDFLAGS) %.out : %.cpp @@ -55,3 +56,5 @@ heap-non-main.out heap-tcache.out heap-multiple-heaps.out: EXTRA_FLAGS := -pthre heap-bins.out: EXTRA_FLAGS := -Wno-unused-result default.out: EXTRA_FLAGS := -fstack-protector-all -fpie -pie + +collision.out: TMPDIR := $(TMPDIR)/collision-libc diff --git a/tests/binaries/collision.c b/tests/binaries/collision.c new file mode 100644 index 000000000..5fa385ba7 --- /dev/null +++ b/tests/binaries/collision.c @@ -0,0 +1,16 @@ +/** + * collision.c + * -*- mode: c -*- + * -*- coding: utf-8 -*- + */ + +#include +#include +#include + + +int main(int argc, char** argv, char** envp) +{ + printf("Hello World!\n"); + return EXIT_SUCCESS; +} diff --git a/tests/regressions/filename_collision_lookup.py b/tests/regressions/filename_collision_lookup.py new file mode 100644 index 000000000..5ebe02662 --- /dev/null +++ b/tests/regressions/filename_collision_lookup.py @@ -0,0 +1,27 @@ +import pytest + +from tests.base import RemoteGefUnitTestGeneric +from tests.utils import debug_target + + +class RegressionFilenameCollisionLookup(RemoteGefUnitTestGeneric): + """Tests for regression about collisions in filenames while using + `process_lookup_path` + + PR: https://github.com/hugsy/gef/pull/1083 + """ + + def setUp(self) -> None: + self._target = debug_target("collision-libc/collision") + return super().setUp() + + def test_process_lookup_path_use_only_filename(self): + root, gdb = self._conn.root, self._gdb + + gdb.execute("start") + program = root.eval("process_lookup_path('collision')") + libc = root.eval("process_lookup_path('libc')") + + assert program is not None + assert libc is not None + assert program != libc