Sometimes in life you will have stupid challenges, like woah I really like that game I hacked previously what if I could plug a chatbot in it.
So first you take a look at your network panel in chrome with the game on.
At this point it doesn’t look like an easy feat, you find some flash ressources but after that all the traffic seems to be encrypted, throwing some kind of beautiful form data and some sample response: OxvfImxOt [...etc] Gtw5PklG+EfbYaQ==
Yep there is definitely some kind of encryption here. The ==
at the end makes you think it's some base64
but no, nice try.
So you open the html source and you find the flash:
<param name="movie" value="http://curvefever.com/CF_Preloader.swf?v=1473686400"/>
Of course you know by your crazy experience from root-me.org that you best friend when it comes to flash is going to be FFDec (Hint: it decompiles flashes)
Long story short, the flash #1 loads a flash #2 using a hardcoded version_int
var urlRequest:URLRequest = new URLRequest(NetConstants.URL + "curvefever.php?version=" + this.version_int);
Then you open the second flash and you know it’s going to be a shitty day when all the variables are obfuscated so you’re left with some beautiful mysteries like:
Don’t panic
You are indeed very dumb to have gone that far so panicking would be of no use, neither would giving up be:
Step1) Find a starting point from some URL / part of the code we know
Step2) Look globally through the code (bonus: try the P-code deobfuscation but that won’t help you here)
So we CMD+F, and search for client_start
(remember the page did a call to that endpoint from the network panel in Chrome)
private function addAccountStart(param1:int, param2:String, param3:uint, param4:uint, param5:uint, param6:uint, param7:String, param8:Boolean, param9:Boolean, param10:String = "") : void
{
D.log("addAccountStart");
var _loc11_:Object = {
"handler":param1,
"name":param2,
"left":param3,
"leftChar":param4,
"right":param5,
"rightChar":param6,
"password":param7,
"guest":(!!param8?1:0),
"development":(!!§_-tY§.§_-e6§?1:0),
"version":§_-tY§.VERSION,
"rememberKey":param10,
"rememberMe":param9
};
this.§_-3n§ = getTimer();
§_-Q2§.§_-HR§("code/client_start_connection.php",_loc11_,this.§_-mt§,this.onAddAccountServerError);
}
Now you take some liters of coffee and deep breaths and you untangle all that crap by guessing who’s who. It’s a puzzle for sadomasochistics grownups of 100+ actionscript files.
After a while you made sense of almost all the vars and calls:
private function addAccountStart(ArrayVectorObjectLength:int, username:String, left:uint, leftChar:uint, right:uint, rightChar:uint, password:String, isPassWordEmpty:Boolean, rememberMeFlagDefaultFalse:Boolean, rememberKeyString:String = "") : void
{
D.log("addAccountStart");
var _configObject_:Object = {
"handler":ArrayVectorObjectLength, // Some function _loc_
"name":username, // username
"left":left, //0
"leftChar":leftChar, // 0
"right":right, // 0
"rightChar":rightChar, // 0
"password":password, // password
"guest":(!!isPassWordEmpty?1:0), // is passempty
"development":(!!CONFIG.IS_DEV_MODE?1:0), // 0
"version":CONFIG.VERSION, // 0.79g
"rememberKey":rememberKeyString, // ""
"rememberMe":rememberMeFlagDefaultFalse // 0
};
this.Timer = getTimer();
§_-DI§.FetchFromUrlAndCallBack("code/client_start_connection.php",_configObject_,this.onAddAccountSuccessStart,this.onAddAccountServerError);
}
On the way you’ve found several hints,
the encryption used by FetchFromUrlAndCallBack
is simple-des-ecb
(which is not the most secure thing we can have out there). And ofc so the client can decrypt, the key is in the SWF code too in the config file.
Another thing is that you’ll need access to the version number that is stored in the final flash (yes it’s a Vegeta technique)
So you spin up a bash that will get it for you because they update often and you don’t want to run FFDec GUI everytime, thank got it provides a .jar
in its Resources:
While you looked around you found that the flash leveraged playerio the (worse oh my god help me it’s unusable) for a gaming API by Yahoo.
Also because we’re in 2017 and they want to be sure nobody can do cool stuff with them, this API can only be used on the desktop client side via:
ActionScript 3 Client Library
Unity3D Client Library
C# Client Library
Fine but I don’t know AS3, Unity3D is too big because I want a simple script and C# is not my thing.
Maybe this project is loved by the community that provided great support? Yes. But no. Only half coded barely working projects.
So I finally find a semi-working protobuf file and playerio.py
in python, I'm happy as a clam so I recode the simple-des-ecb
encrypt/decrypt to get the connection auth token, and voilà, I can list rooms now!
But when I tried to join I got thrown out.
This is because the python library I used didn’t have all the functionality I needed… So I recoded most of the code in C# (Xamarin Studio is very efficient) and made use of the PlayerIO Framework.
Tips: If you have a C# code on Xamarin that is supposed to wait on the Console.ReadLine();
instead of closing right away you should edit the settings and add --console
as launch parameter and check launch external console.
So now we recode all the connection in C#, install the official PlayerIO SDK and implements the steps exactly like in the Flash version (at least what we understand of it from deobfuscating).
1) Get the version number
2) Send client_start_connection
3) Connect to the reception room
4) Create a FriendList room
5) Connect to the Service room
6) Send ping from the service room
7) Connect to the European Cluster
8) Use return from 2) to craft client_get_hash call to enter a room
9) Send EVENT when game start to not get disconnected
And here we go, CurveBot1 finally joins the game room!
As expected our little fella now sees all the events the clients are capable of receiving:
As it turned out events from the game were actually broadcasted… in advance… so players could accomodate with the lag, that meant I was able to see the future items spawns and holes in the game in advance, giving me, godlike capacities such as being able to do jumps over my own curve perfectly timed:
Even with all that I was unable to connect my bot as a non-Guest user. Sigh. But heh, he can still tell me some stuff at least
Quick note on CSharp
I hate you C#. It took me ages to understand how to work with JSON. Looping through objects is hell, the examples with PlayerIO were slim but ok, your IDE is very nice.
So for the people that might need it with JSON, just add some dynamic
everywhere until it works.
dynamic code = JObject.Parse(json);
dynamic player = code.players;
player[0]["defColor"] = 0;
player[0]["clanTag"] = "";
player[0]["iconId"] = 0;
player[0]["mutualFriends"] = ((dynamic) this.playerDict["friends"]).mutual;
return JsonConvert.SerializeObject(player);
Escaping C# and back to JS
One of the first things I wanted was to escape the C# world to get back to the JS where I’m more comfortable with, here came Process
. So that any user feature responding to text was forwarded to a nodejs program then back.
Next step, making the bot play too!