vBulletin Large Inline Images “Exploit”

Disclaimer: this does not affect newer versions of vBulletin! Only old versions are affected, and only if you have no maximum character limit in posts.

Our forum brand, ForumOps works on a lot of vBulletin forums, primarily running on 3.8.x. We ran into quite an annoying “exploit” (perhaps not technically, but it’s caused us problems) that older communities should be interested in.

Whenever you copy/paste an image into the editor, it gets converted into an inline image. This makes people say “wow!” because it’s so much more convenient than using the attachment system or than using a 3rd party image uploading service. vBulletin’s old WYSIWYG editor uses inline images, which get inserted directly into the post with base64 encoding. There are a few major concerns here…

However, in order for this to cause problems, you need to have the post character limit disabled. This is quite common, since without a technical explanation, why would you want to limit the post length? Some people post long articles and are frustrated by it. Usually, the inline image is harmless… people who copy/paste rich content typically only paste in small images or icons, which jut work.

If you post a large image, and have the character limit disabled, it can cause a myriad of problems.

Database – Let’s say an average post is 1000 characters. That is approximately 1000 bytes, or roughly 1 kilobyte (assuming they are standard English characters). Once you paste an image in, that post could now be 1MB, or even 10MB (depending on multiple PHP settings). 1MB is a fairly reasonable image size that we see quite often. With a single image embedded in the post, that post is now takes up 1000x space. If you had a few images, or a larger image, it could be up to 100,000x larger! People tend to see performance problems once they get a few hundred thousand posts, and especially once they get into the millions. If you have a few posts, you will be hitting those limits a lot faster than you’d expect. This can create some obvious database issues with performance and maintainability. This may even cause your post table to crash and give you a ton of hard to debug problems. A DBA will notice the table is huge, but will just assume it’s because of all the post content.

vBulletin Code – The biggest problem we ran into was with the BB code parser. Instead of it having to parse 1kb of text, it now has ~1000x more text to process, which is brutally slow. This makes viewing that broken thread impossible since it will likely time out, or just hang forever (long enough for people to give up, anyway). If it’s buried in a popular thread, it may even be near impossible to remove, since it’s hard to delete posts without actually going into the specific thread.

Now, our client was lucky since the problem was ran into on accident. A user had posted a 5MB image, and we received complaints about certain parts of the site being broken (BB Code parser showing new posts, and in a custom moderator section). A malicious user could easily use this to bring the site down rapidly, giving the moderators little to no chance of fighting back.

The Solution – If you insist on disabling your post limit, please consider setting it high (even very high; say 500,000) since users could still post a huge post and cause issues. Images are a little trickier since it’s unintentional. However, people are set in their ways, so lets at least try to mitigate the root problem…

If you create a new plugin using the “newpost_process” hook, and use the following code, it should prevent attacks.

if (strpos($post['message'], 'data:image') !== false and strpos($post['message'], 'base64') !== false) {
    $maxSize   = 1024*500;
    $totalSize = 0;
    $matches   = null;
 
    preg_match_all(
    	'#data\:image/(gif|png|jpg|jpeg);base64,([0-9a-zA-Z\+\/\=]+)\[/(img|IMG)\]#', 
        $post['message'], 
        $matches,
        PREG_SET_ORDER
    );
 
    foreach ($matches as $match) {
        $totalSize += strlen($match[2]);
    }
 
    if ($totalSize > $maxSize) {
        $dataman->errors[] = sprintf(
            trim("
            	You've copy-pasted some images in your post which are %s too large.
            	Please remove them and add them as attachments instead.
            "),
    	    vb_number_format(strlen($post['message']) - $maxSize, 0, true)
        );
    }
}

This checks for the existence of inline images within a post, and can limit them to a specific file size. In the above code, I have it set at 500kb, which is probably a little high. Feel free to tweak the error message the user will receive.

I have no idea which versions this affects. I know it works on 3.x, and perhaps some of the earlier 4.x versions before the editor was swapped out. If anyone can verify it for specific versions, I’ll be sure to mend my post.

Hope this helps someone out there!