qilingframework / qiling

A True Instrumentable Binary Emulation Framework
https://qiling.io
GNU General Public License v2.0
5.14k stars 744 forks source link

Emulated code can write files outside of rootfs (directory traversal bug) #572

Closed nmantani closed 4 years ago

nmantani commented 4 years ago

Hello,

I found that Qiling Framework 1.1.3 has a directory traversal bug that an emulated code can write files outside of rootfs. When I executed a sample code emulation_test.py, files were written in the following locations outside of rootfs directory (C:\Users\User\Desktop\rootfs\x8664_linux).

Sample code (emulation_test.py)

import qiling

file_path = "C:\\Users\\User\\Desktop\\write-test"
rootfs_path = "C:\\Users\\User\\Desktop\\rootfs\\x8664_linux"

ql = qiling.Qiling(filename=[file_path], rootfs=rootfs_path, output="debug", profile="linux.ql")
ql.run()

Sample code (write-test.c)

#include <stdio.h>

int main(void)
{
    FILE *f;

    f = fopen("../test1.txt", "w");
    fputs("test1\n", f);
    fclose(f);

    f = fopen("../../test2.txt", "w");
    fputs("test2\n", f);
    fclose(f);

    f = fopen("../../../test3.txt", "w");
    fputs("test3\n", f);
    fclose(f);

    return 0;
}

Compilation of write-test.c (on WSL1)

user@PC:/mnt/c/Users/user/Desktop$ cc -static -o write-test write-test.c
user@PC:/mnt/c/Users/user/Desktop$

Output of emulation_test.py

PS C:\Users\user\Desktop> py -3 .\emulation_test.py
[+] load 0x400000 - 0x4b6000
[+] load 0x6b6000 - 0x6bc000
[+] load 0x6bc000 - 0x6bd000
[+] mem_start: 0x400000 mem_end: 0x6bd000
[+] mmap_address is : 0x7fffb7dd6000
brk(0x0)
[+] brk return(0x6bf000)
brk(0x6c01c0)
[+] brk return(0x6c1000)
arch_prctl(0x6bf880) = 0
uname(0x80000000dbd0) = 0
readlink(\proc\self\exe, 0x80000000cd00, 0x1000) = -1
access(\etc\ld.so.nohwcap, 0x0) = -1
[!] No such file or directory
brk(0x6e2000)
[+] brk return(0x6e2000)
openat(4294967196, \..\test1.txt, 0x8301, 0o666) = 3
[+] openat(4294967196, \..\test1.txt, O_RDONLY | O_WRONLY | O_NOCTTY | O_TRUNC | 32768, 0o666) = 3
[+] File Found: C:\Users\User\Desktop\rootfs\x8664_linux\..\test1.txt
fstat(3, 0x80000000dbd0) = 0
[+] fstat write completed
write(3,6c16b0,6) = 0
close(3) = 0
openat(4294967196, \..\..\test2.txt, 0x8301, 0o666) = 3
[+] openat(4294967196, \..\..\test2.txt, O_RDONLY | O_WRONLY | O_NOCTTY | O_TRUNC | 32768, 0o666) = 3
[+] File Found: C:\Users\User\Desktop\rootfs\x8664_linux\..\..\test2.txt
fstat(3, 0x80000000dbd0) = 0
[+] fstat write completed
write(3,6c16b0,6) = 0
close(3) = 0
openat(4294967196, \..\..\..\test3.txt, 0x8301, 0o666) = 3
[+] openat(4294967196, \..\..\..\test3.txt, O_RDONLY | O_WRONLY | O_NOCTTY | O_TRUNC | 32768, 0o666) = 3
[+] File Found: C:\Users\User\Desktop\rootfs\x8664_linux\..\..\..\test3.txt
fstat(3, 0x80000000dbd0) = 0
[+] fstat write completed
write(3,6c16b0,6) = 0
close(3) = 0
exit_group(0)
PS C:\Users\user\Desktop>

How to fix

It seems that convert_for_native_os(), convert_posix_to_win32(), and convert_win32_to_posix() that are called from convert_path() in qiling/os/utils.py should normalize a path (for example, converting from "/../test1.txt" to "/test1.txt") when it is concatenated with rootfs path.

Current convert_for_native_os() in utils.py:

 @staticmethod
    def convert_for_native_os(rootfs, cwd, path):
        rootfs = Path(rootfs)
        cwd = PurePosixPath(cwd[1:])
        path = Path(path)
        if path.is_absolute():
            return rootfs / path.relative_to(path.anchor)
        else:
            return rootfs / cwd / path
xwings commented 4 years ago

Hi,

Cool, will you be able to make a PR ?

nmantani commented 4 years ago

OK, I will try to write a fix and make a PR. Please review it later.

nmantani commented 4 years ago

Hi,

I have made PR #574.

xwings commented 4 years ago

thanks !

xwings commented 4 years ago

conversation moved to PR #574