Thursday, March 28, 2013

Give a man a fish...


I guess ArenaNet has had time enough to react and fix their broken security, so it's
time to explain what exactly they messed up.

As I showed in a previous post, the game connection uses diffie hellman to negotiate the RC4 session key but stores the server's side pre-calculated in the client.

So how in hell did I calculate the server's secret?! ie. For the current release (17368) where do I get this from:

const unsigned char p[] = {
0xa1, 0xc5, 0x67, 0x7b, 0x95, 0xef, 0x74, 0x12, 0xcf, 0x58, 0xab, 0xef, 0xb0, 0x60, 0x9e, 0xa7,
0xab, 0x6c, 0x15, 0x3c, 0x6d, 0xf0, 0x24, 0xac, 0x0d, 0x02, 0x26, 0x66, 0xd1, 0x65, 0x28, 0x53,
0x29, 0xc7, 0xb4, 0xc4, 0x7b, 0xdd, 0xbc, 0x4c, 0x8d, 0xab, 0x7d, 0x6a, 0x7e, 0x26, 0xd7, 0x3c,
0x62, 0xe5, 0x66, 0x5f, 0x38, 0x9f, 0x49, 0x44, 0x6b, 0x18, 0x74, 0x9c, 0x43, 0x7a, 0xf4, 0x17,
};


const unsigned char b[] = {
0x8e, 0xe7, 0x79, 0xf0, 0x00, 0x0d, 0x31, 0xc9, 0xda, 0x45, 0x77, 0x44, 0xd3, 0x47, 0x0a, 0x26,
0x8d, 0x7e, 0xe3, 0x03, 0x02, 0x55, 0x6b, 0x0d, 0x88, 0xc7, 0x65, 0x32, 0x29, 0xbe, 0x1d, 0x71,
0x9f, 0x49, 0x00, 0xf9, 0x65, 0x97, 0xe6, 0x05, 0xda, 0x3b, 0x8f, 0xc0, 0xc7, 0x0b, 0x3a, 0x49,
0xe3, 0xfa, 0x6e, 0x2e, 0x27, 0x54, 0xab, 0x13, 0xae, 0xf5, 0x54, 0x61, 0x29, 0x4f, 0x37, 0x35,
};



To explain it.. we have to look at how the game does it's updates.

ArenaNet is using a content delivery network to distribute game updates. At startup, the game patcher will query an URL on the CDN which contains information about what the current live version is.

This information is at:
  http://assetcdn.101.ArenaNetworks.com/latest/101


Now, if you try to access this with your browser, you'll get an authentication error. To pass the authentication check, a header called 'authCookie' has to be passed. Older clients had this header clearly visible in the executable, ie:

$ strings gw2.exe.524178 |grep md5=|cut -c1-
authCookie=access=/latest/*!/manifest/program/*!/program/*~md5=4e51ad868f87201ad93e428ff30c6691

In newer clients it's build through printf style formatting, so it's a bit more effort to get it:

$ strings gw2.exe.570713 |grep md5=|cut -c1-
authCookie=access=/latest/*!/manifest/program/*!/program/*~md5=%s

However, the md5 value is always the same (haven't verified this on the latest clients anymore), so you can just use this value.

$ curl http://assetcdn.101.ArenaNetworks.com/latest/101 -b 'authCookie=access=/latest/*!/manifest/program/*!/program/*~md5=4e51ad868f87201ad93e428ff30c6691'
17368 570713 22680128 570709 5108


This tells us the latest release is 17368, has a fileid of 570713 on the CDN and is 22Mb in size. Equipped with this information, we can download the latest gw2 executable:

$ curl http://assetcdn.101.ArenaNetworks.com/program/101/1/0/570713 -b 'authCookie=access=/latest/*!/manifest/program/*!/program/*~md5=4e51ad868f87201ad93e428ff30c6691' -o gw2.exe

Easy enough...


An update contains much more than just the gw2 executable, there's also patches for the data files and other stuff.

And this is where ArenaNet made a gigantic mistake in my opinion. If you take the previous link and  increment the filenumber by one, you get something VERY interesting! For some unknown reason,  the actual server code and authentication code is also on the CDN and it's even accessible to everyone that wants a copy!!

If you take fileid 570714 and run it through objdump, you will see it's actually called GwSrv.dll and fileid 570715 is called AuthSrv.dll

Imagine my surprise when I discovered this. By that time, I had already reversed the encryption protocols used on both connections, so it didn't help me for that part, but I also knew one of those 2 DLLs would need access to the server side secret and after some investigating, it was clear the key was contained inside the DLL itself (it could aswell have been loaded from an external file).

Equipped with that info, I wrote a small tool using libbfd which looks for the keys and just dumps them. I later verified they were in fact correct.


To this day, I'm still gobsmacked that a company can make such an error. Makes you wonder how they store all the credit card information of people!

The GwSrv.dll will be particularly interesting for people trying to write an emulator, but it's also nice to get an idea of how their backend is setup.

ie.

$ strings GwSrv.dll |grep -P '^[A-Z].*Srv$'
FileSrv
BuildSrv
DllSrv
AuthSrv
GwSrv
DbSqlSrv
DbCacheSrv
StreamSrv
SiteSrv
DistSrv
AcctHttpSrv
AcctMailSrv
CrashDumpSrv
EventLogSrv
LogQuerySrv
BeaconSrv
CrashSrv
UTournSrv
ConfigSrv
DbNameSrv
BugMailSrv
LogImportSrv
CensusSrv
DummyCliSrv
DbNameTestSrv
DbSrv
RankSrv
FileIdChkSrv
IpBlockSrv
PitchingSrv
ConfigSrv
ObsCtrlSrv
GuildSrv
ReplicationSrv
AssetSrv
EconDataSrv
EconSrv
ClientErrorLogSrv
TournSrv
EmptySrv
PlayerlessSrv
BrokerSrv
ExchangeSrv
GemStoreSrv
TradeSrv
CharUserDataSrv
PortalSrv
HealthSrv
ClientErrorSrv
MetricsSrv
ItemSearchSrv
ItemDataSrv
RestoreSrv
ListTaskSrv
TournSearchSrv
ContentSrv

Pity they didn't put them all on the CDN ;-)


No comments: