Skip to content

Commit

Permalink
Add fix and example for openat path traversion
Browse files Browse the repository at this point in the history
  • Loading branch information
wtdcode committed Jan 24, 2022
1 parent bba26a5 commit 6d0fc4a
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 11 deletions.
2 changes: 1 addition & 1 deletion examples/rootfs
15 changes: 15 additions & 0 deletions 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 <fcntl.h>
#include <unistd.h>

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);
}
5 changes: 1 addition & 4 deletions qiling/os/mapper.py
Expand Up @@ -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)

Expand Down
6 changes: 4 additions & 2 deletions qiling/os/path.py
Expand Up @@ -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):
Expand Down
11 changes: 7 additions & 4 deletions qiling/os/posix/syscall/fcntl.py
Expand Up @@ -4,6 +4,7 @@
#

import os
from pathlib import Path

from qiling import Qiling
from qiling.const import QL_OS, QL_ARCH
Expand Down Expand Up @@ -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:
Expand Down
9 changes: 9 additions & 0 deletions tests/test_elf.py
Expand Up @@ -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()

0 comments on commit 6d0fc4a

Please sign in to comment.