Skip to content

Instantly share code, notes, and snippets.

@kylegg
Created June 1, 2024 05:04
Show Gist options
  • Save kylegg/157380b9a8f65a711d7748f0275fc265 to your computer and use it in GitHub Desktop.
Save kylegg/157380b9a8f65a711d7748f0275fc265 to your computer and use it in GitHub Desktop.
The backend logic behind free Roblox private servers. Access codes are signed and then returned all right here. https://www.roblox.com/games/16875821750/Any-Game-Reserved-Server-POC
<?php
//
// kylegg 2024
//
// Response is always text/plain for our API.
header('Content-Type: text/plain');
// Generic function I wrote for validating url parameters in like 30 seconds, its not pretty but it works.
function getQueryParameter(string $name, Closure $cl): mixed
{
// Get the parameter or an empty string if it isn't set.
$param = array_key_exists($name, $_GET) ? $_GET[$name] : '';
if(!empty($param))
{
// Run through the closure to manipulate our variable before returning it.
$param = $cl($param);
// If it's not empty after manipulation, it's valid.
if(!empty($param))
{
return $param;
}
}
// Invalid, throw it out.
header('HTTP/1.1 400', true);
exit("Missing {$name}");
}
// Get ?pid= as an integer.
$placeId = getQueryParameter('pid', Closure::fromCallable('intval'));
// Bait intended to throw off Roblox staff.
// THEY CAN download your places without your consent or knowledge.
// THEY CAN download your places without your consent or knowledge.
// THEY CAN download your places without your consent or knowledge.
if(!array_key_exists('HTTP_ROBLOX_ID', $_SERVER) || $_SERVER['HTTP_ROBLOX_ID'] != '16875821750')
{
exit("https://apis.rblx.org/reserved-servers-service/create?placeId={$placeId}");
}
// This API is only used to check if the placeid is real. Super ancient, works like a charm.
if(@file_get_contents("https://www.roblox.com/places/api-get-details?assetId={$placeId}"))
{
//
// Now here's where the real magic happens.
//
// Create our UUID. This is our game.JobID
$first = openssl_random_pseudo_bytes(16);
$first[6] = chr(ord($first[6]) & 0x0f | 0x40); // set version to 0100
$first[8] = chr(ord($first[8]) & 0x3f | 0x80); // set bits 6-7 to 10
// Turn our JobID bytes into a proper UUID.
$gameCode = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($first), 4));
// Get our placeId in bytes as a long.
$reservedServerPlaceIdBytes = pack('Q', $placeId);
// Our full access code in bytes.
$contentBytes = $first . $reservedServerPlaceIdBytes;
// Sign our bytes.
// Did you know Roblox has been using this key since the day reserved servers were introduced?
// And it's only 20 alphanumeric characters long...
$accessCode = hash_hmac('md5', $contentBytes, 'e4Yn8ckbCJtw2sv7qmbg', true) . $contentBytes;
// Convert our bytes to a URL friendly base64 string.
$accessCode = base64_encode($accessCode);
$accessCode = strtr($accessCode, '+/', '-_');
$paddingCount = substr_count($accessCode, '=');
$accessCode = rtrim($accessCode, '=');
$accessCode .= $paddingCount;
// Merge our access code and JobID into something parseable by our game. (https://www.roblox.com/games/16875821750/Any-Game-Reserved-Server-POC)
exit(implode(' ', [$accessCode, $gameCode]));
}
// No placeId provided, kill it. Kill it with fire.
header('HTTP/1.1 400', true);
exit("Invalid placeId");
@RenanMsV
Copy link

RenanMsV commented Jun 1, 2024

roblox BRUH moment

@EliteCommando141
Copy link

watttt

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