Skip to content

Instantly share code, notes, and snippets.

@Yakov5776
Last active May 2, 2024 03:27
Show Gist options
  • Save Yakov5776/b0ebba5f2f86ea8707fd3f7a85a78ba1 to your computer and use it in GitHub Desktop.
Save Yakov5776/b0ebba5f2f86ea8707fd3f7a85a78ba1 to your computer and use it in GitHub Desktop.
Roblox 2012-2018 Ticket authentication remote-code execution rundown

Summary

ClientTicket property extracted from a 2016 joinscript:

"ClientTicket":"5/14/2016 11:39:03 AM;hxgfjpUzqqdYH+A32XmSIUYQD0YIB6uw1+msiEq8A1Ymxv69JEkpqa0f4ppko6gN8gsQbOduUE/S0Wpu1+aNltDWzzyeNsULRuvpbePLzlKs5xrajpGGosjYB9wuXUa4WoBzMrvZ8mr4Takg107piXCCtFtnoBw1pn2CNIv+Fs8=;hwl1N/ApxZ+tNf9ccMmocz7VqeIfwWd4hZ8tR60Wyph3UXj+qjOt3IlYNq6+9jOB5+jY1pspSoh/3Xs863O+Ifo55b/RO2yL7g6Sk4Evqhn2/ImoXUSYQD5XjbKm3INa2Hd6oJFO2l1u1NaYvu+4shP6JMIBL8GMeRQYFEvJX2Q="

Snippet of code which handles grabbing signature from ticket/ClientTicket (on rccsecurity) after received from client (after received from joinscript request):

std::vector<std::string> s;
boost::split(s, ticket, boost::is_any_of(";"));
std::string signature = s[1]; // or s[2] if at second stage of ticket verifying
Crypt().verifySignatureBase64(signature);

Shortened version of Crypt::verifySignatureBase64():

bool Crypt::verifySignatureBase64(std::string signatureBase64)
{
    bool result = false;
    HCRYPTHASH hash;
    if (!CryptCreateHash(context, CALG_SHA1, NULL, 0, &hash))
    {
        int signatureLen = Base64DecodeGetRequiredLength(signatureBase64.size());
        BYTE* signature = (BYTE*)alloca(signatureLen);
        ATL::Base64Decode(signatureBase64.c_str(), signatureBase64.size(), signature, &signatureLen);
        
        BYTE signatureRev[10240];
        for (int i=0; i<signatureLen; ++i)
            signatureRev[i] = signature[signatureLen - i - 1];

        if (CryptVerifySignature(hash, signatureRev, signatureLen, key, NULL, 0))
            result = true;
    }
    ::CryptDestroyHash(hash);
    return result;
}

The Problem

signatureRev[10240] uses a fixed-size buffer, which lets you write over buffer if the given signature derived from the client exceeds the size. exploiting this could lead to gaining access & control over the server running rcc.

The Solution

One thing you could do is check if signatureLen is beyond the allocated array size limit.

Alternatively, you could use a dynamically allocated array system like vectors:

std::vector<BYTE> signatureRev(signatureLen);
for (int i = 0; i < signatureLen; ++i)
    signatureRev[i] = signature[signatureLen - i - 1];

Proof-of-Concept: (exploit)

todo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment