From 6d0fc4a81880abc2984552ccd23497d8832d00fe Mon Sep 17 00:00:00 2001 From: lazymio Date: Mon, 24 Jan 2022 15:15:34 +0100 Subject: [PATCH 1/4] Add fix and example for openat path traversion --- examples/rootfs | 2 +- examples/src/linux/path_traverse.c | 15 +++++++++++++++ qiling/os/mapper.py | 5 +---- qiling/os/path.py | 6 ++++-- qiling/os/posix/syscall/fcntl.py | 11 +++++++---- tests/test_elf.py | 9 +++++++++ 6 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 examples/src/linux/path_traverse.c diff --git a/examples/rootfs b/examples/rootfs index a489ff519..149041a15 160000 --- a/examples/rootfs +++ b/examples/rootfs @@ -1 +1 @@ -Subproject commit a489ff519f570539249c34c7d1bd5abb3ec74488 +Subproject commit 149041a153ad8b5366b1b070afb20e2f6293aaf6 diff --git a/examples/src/linux/path_traverse.c b/examples/src/linux/path_traverse.c new file mode 100644 index 000000000..6d478e670 --- /dev/null +++ b/examples/src/linux/path_traverse.c @@ -0,0 +1,15 @@ +// gcc -static ~/qiling/examples/src/linux/path_traverse.c -g -O0 -o ~/qiling/examples/rootfs/x8664_linux/bin/path_traverse_static +// https://www.kalmarunionen.dk/writeups/2022/rwctf/qlaas/ +#include +#include + +int main(){ + char buf[4096]; + int fd = openat(1, "/etc/passwd", O_RDONLY); + ssize_t len = read(fd, buf, sizeof(buf)); + write(1, buf, len); + + fd = openat(1, "/etc/passwd_link", O_RDONLY); + len = read(fd, buf, sizeof(buf)); + write(1, buf, len); +} diff --git a/qiling/os/mapper.py b/qiling/os/mapper.py index e5e9382dd..738e51d38 100644 --- a/qiling/os/mapper.py +++ b/qiling/os/mapper.py @@ -95,14 +95,11 @@ def has_mapping(self, fm): def mapping_count(self): return len(self._mapping) - def open_ql_file(self, path, openflags, openmode, dir_fd=None): + def open_ql_file(self, path, openflags, openmode): if self.has_mapping(path): self.ql.log.info(f"mapping {path}") return self._open_mapping_ql_file(path, openflags, openmode) else: - if dir_fd: - return ql_file.open(path, openflags, openmode, dir_fd=dir_fd) - real_path = self.ql.os.path.transform_to_real_path(path) return ql_file.open(real_path, openflags, openmode) diff --git a/qiling/os/path.py b/qiling/os/path.py index 9872e4f30..e58273366 100644 --- a/qiling/os/path.py +++ b/qiling/os/path.py @@ -133,8 +133,10 @@ def transform_to_real_path(self, path: str) -> str: if os.path.islink(real_path): link_path = Path(os.readlink(real_path)) - if not link_path.is_absolute(): - real_path = Path(os.path.join(os.path.dirname(real_path), link_path)) + real_path = self.convert_path(self.ql.rootfs, os.path.dirname(real_path), link_path) + + if os.path.islink(real_path): + real_path = self.transform_to_real_path(real_path) # resolve multilevel symbolic link if not os.path.exists(real_path): diff --git a/qiling/os/posix/syscall/fcntl.py b/qiling/os/posix/syscall/fcntl.py index e7f515839..fe672ce9e 100644 --- a/qiling/os/posix/syscall/fcntl.py +++ b/qiling/os/posix/syscall/fcntl.py @@ -4,6 +4,7 @@ # import os +from pathlib import Path from qiling import Qiling from qiling.const import QL_OS, QL_ARCH @@ -101,11 +102,13 @@ def ql_syscall_openat(ql: Qiling, fd: int, path: int, flags: int, mode: int): fd = ql.unpacks(ql.pack(fd)) if 0 <= fd < NR_OPEN: - dir_fd = ql.os.fd[fd].fileno() - else: - dir_fd = None + fobj = ql.os.fd[fd] + # ql_file object or QlFsMappedObject + if hasattr(fobj, "fileno") and hasattr(fobj, "name"): + if not Path.is_absolute(Path(file_path)): + file_path = Path(fobj.name) / Path(file_path) - ql.os.fd[idx] = ql.os.fs_mapper.open_ql_file(file_path, flags, mode, dir_fd) + ql.os.fd[idx] = ql.os.fs_mapper.open_ql_file(file_path, flags, mode) regreturn = idx except QlSyscallError as e: diff --git a/tests/test_elf.py b/tests/test_elf.py index 86a35a5f2..e4f84378f 100644 --- a/tests/test_elf.py +++ b/tests/test_elf.py @@ -1091,6 +1091,15 @@ def test_memory_search(self): self.assertEqual([0x1000 + 11, 0x2000 + 11, 0x3000 + 43], ql.mem.search(b"\x09\x53(\x0f|\x1a|\x04)[^\x0d]")) del ql + + def test_elf_linux_x8664_path_traversion(self): + mock_stdout = pipe.SimpleOutStream(sys.stdout.fileno()) + ql = Qiling(["../examples/rootfs/x8664_linux/bin/path_traverse_static"], "../examples/rootfs/x8664_linux", verbose=QL_VERBOSE.DEBUG, stdout=mock_stdout) + + ql.run() + self.assertTrue("root\n" not in ql.os.stdout.read().decode("utf-8")) + + del ql if __name__ == "__main__": unittest.main() From 12aaf2b1fe8a669f7411d5a4402c321422659ea3 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sun, 13 Feb 2022 10:22:15 +0100 Subject: [PATCH 2/4] Update rootfs --- examples/rootfs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rootfs b/examples/rootfs index 149041a15..d8a9b0d6c 160000 --- a/examples/rootfs +++ b/examples/rootfs @@ -1 +1 @@ -Subproject commit 149041a153ad8b5366b1b070afb20e2f6293aaf6 +Subproject commit d8a9b0d6c52a3c5bc627c055d5f711dacbb1a1f6 From f6a0f121c7b9576bf28628664a097b63a31b4038 Mon Sep 17 00:00:00 2001 From: lazymio Date: Sun, 13 Feb 2022 10:55:31 +0100 Subject: [PATCH 3/4] Fix link path resolve --- qiling/os/path.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qiling/os/path.py b/qiling/os/path.py index e58273366..9102d7458 100644 --- a/qiling/os/path.py +++ b/qiling/os/path.py @@ -133,7 +133,7 @@ def transform_to_real_path(self, path: str) -> str: if os.path.islink(real_path): link_path = Path(os.readlink(real_path)) - real_path = self.convert_path(self.ql.rootfs, os.path.dirname(real_path), link_path) + real_path = self.convert_path(os.path.dirname(real_path), "/", link_path) if os.path.islink(real_path): real_path = self.transform_to_real_path(real_path) From f9ad909a2ea03f5decccce488ccdfa6e7003cb1c Mon Sep 17 00:00:00 2001 From: lazymio Date: Sun, 13 Feb 2022 10:56:02 +0100 Subject: [PATCH 4/4] Add a TODO for VFS --- qiling/os/path.py | 1 + 1 file changed, 1 insertion(+) diff --git a/qiling/os/path.py b/qiling/os/path.py index 9102d7458..b3914bcac 100644 --- a/qiling/os/path.py +++ b/qiling/os/path.py @@ -128,6 +128,7 @@ def transform_to_link_path(self, path: str) -> str: return str(real_path.absolute()) def transform_to_real_path(self, path: str) -> str: + # TODO: We really need a virtual file system. real_path = self.convert_path(self.ql.rootfs, self.cwd, path) if os.path.islink(real_path):