Skip to content

Instantly share code, notes, and snippets.

@GarboMuffin
Last active March 2, 2026 04:57
Show Gist options
  • Select an option

  • Save GarboMuffin/5c71103133f8fd576b2752a927a03009 to your computer and use it in GitHub Desktop.

Select an option

Save GarboMuffin/5c71103133f8fd576b2752a927a03009 to your computer and use it in GitHub Desktop.
This decrypts projects from the cocrea.world projects API. Claude and I reverse engineered the website to figure out how this works, then Claude took our learnings and made a script.
// how to break the decryption
const https = require('https');
const crypto = require('crypto');
const PROJECT_URL = 'https://www.cocrea.world/@RedLzer2048/SprunkiHyperShifted';
function fetch(url) {
return new Promise((resolve, reject) => {
https.get(url, { headers: { 'User-Agent': 'Mozilla/5.0' } }, res => {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
return fetch(res.headers.location).then(resolve, reject);
}
const chunks = [];
res.on('data', c => chunks.push(c));
res.on('end', () => resolve(Buffer.concat(chunks)));
res.on('error', reject);
}).on('error', reject);
});
}
function decrypt(base64Data, assetId) {
const secret = 'KzdnFCBRvq3';
const keyBuf = Buffer.from(secret + assetId, 'base64');
const key = keyBuf.slice(0, 32);
const iv = keyBuf.slice(0, 16);
const ciphertext = Buffer.from(base64Data, 'base64');
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
// Decrypted text is comma-separated byte values
return Buffer.from(decrypted.toString('utf-8').split(',').map(Number));
}
(async () => {
// Step 1: Fetch the page and extract projectLink from __NEXT_DATA__
console.log('Fetching page...');
const html = (await fetch(PROJECT_URL)).toString('utf-8');
const nextData = JSON.parse(html.match(/<script id="__NEXT_DATA__"[^>]*>(.*?)<\/script>/)[1]);
const projectLink = nextData.props.pageProps.creationData.creationReleaseResp.projectLink;
console.log('Project link:', projectLink);
// Step 2: Extract assetId from the URL (the filename without .sb3)
const assetId = projectLink.match(/\/([a-f0-9]+)\.sb3/)[1];
console.log('Asset ID:', assetId);
// Step 3: Download and decrypt
console.log('Downloading encrypted sb3...');
const encrypted = await fetch(projectLink);
const first8 = Array.from(encrypted.slice(0, 8));
let sb3;
if (first8[0] === 80 && first8[1] === 75) {
// Already a valid zip
sb3 = encrypted;
} else {
console.log('Decrypting...');
sb3 = decrypt(encrypted.toString('utf-8'), assetId);
}
console.log('Decrypted:', sb3.length, 'bytes, starts with', Array.from(sb3.slice(0, 4)));
const fs = require('fs');
fs.writeFileSync('/tmp/project.sb3', sb3);
console.log('Wrote /tmp/project.sb3');
})();
// paste this into your JavaScript console to download the project as an sb3 (project must already be loaded)
function getVM() {
const root = document.querySelector('#__next');
const key = Object.keys(root).find(k => k.startsWith('__reactContainer$') || k.startsWith('__reactFiber$'));
function search(obj, d) {
if (!obj || typeof obj !== 'object' || d > 5) return;
const seen = new WeakSet();
const stack = [{ o: obj, d }];
while (stack.length) {
const { o, d } = stack.pop();
if (!o || typeof o !== 'object' || d > 5 || seen.has(o)) continue;
seen.add(o);
if (typeof o.start === 'function' && typeof o.greenFlag === 'function') return o;
for (const k of Object.keys(o)) {
try { if (o[k] && typeof o[k] === 'object') stack.push({ o: o[k], d: d + 1 }); } catch(e) {}
}
}
}
const fiberSeen = new WeakSet();
function walk(node, depth) {
if (!node || depth > 100 || fiberSeen.has(node)) return;
fiberSeen.add(node);
for (const k of ['memoizedProps', 'memoizedState'])
{ const r = search(node[k], 0); if (r) return r; }
if (node.stateNode && typeof node.stateNode === 'object')
for (const k of ['props', 'state'])
{ const r = search(node.stateNode[k], 0); if (r) return r; }
return walk(node.child, depth + 1) || walk(node.sibling, depth + 1);
}
return walk(root[key], 0);
}
const vm = getVM();
vm.saveProjectSb3().then(blob => {
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = document.title.replace(/[^a-zA-Z0-9 ]/g, '').trim() + '.sb3';
a.click();
URL.revokeObjectURL(url);
console.log('Download started:', a.download, (blob.size / 1024 / 1024).toFixed(1) + ' MB');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment