Showing posts with label gw2. Show all posts
Showing posts with label gw2. Show all posts

Saturday, April 13, 2013

Fixed!

So I was finally contact by ArenaNet regarding the issue I mentioned. It seems my original mail regarding the issue I posted about, was unfortunately lost in cyberspace. So they never read my report.

Anyway, I must say I was pleasantly surprised by the way they handled it. Respect!

Thursday, April 11, 2013

Fixed?!

"We've updated the game for routine maintenance only. No content changes were included. ~RB2"
-- https://twitter.com/GuildWars2/status/322082110334697472

It seems the GwSrv and AuthSrv libraries aren't on the CDN. I wonder if they finally fixed their leak?! Sad that mailing them didn't work, but posting about it got the job done... The old files however are still present.



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 ;-)


Monday, March 11, 2013

Portal Connection - update

Something I just noticed today when investigating an IMAP SSL connection issue (using openssl's s_client), is that the handshake packets looked quite similar to the SSL setup of the gw2 portal connection.

ie. what I originally had:

0x14 = ack
0x15 = server error
0x16 = auth phase
0x17 = game packet

is actually:


enum {
       change_cipher_spec(20), alert(21), handshake(22),
       application_data(23), (255)
   } ContentType;
 
As shown in RFC5246 - The Transport Layer Security (TLS) Protocol, makes sense actually that they used a library for that instead of rolling their own TLS version.

Friday, March 8, 2013

Game connection

So last time I talked about the portal connection, now it's time to go over the security on the game connection.

The guildwars2 game connection is encrypted with RC4. Both the send and receive direction start from the same key.

The session key is randomly generated. The exchange of the session key is done using Diffie Hellman key exchange. The server's B value (gb mod p) is pre-computed and stored inside the game client, therefor isn't transmitted during the setup of the connection.


When the connection is established, the client will send a packet with version information and then send it's diffie hellman component to the server, ie. value A ( ga mod p). The value for g is fixed in all versions upto now and is always 4. The value p is changed on each new build of the game.

The actual session key is the computed diffie hellman shared secret xor'ed with the 20 bytes block the server sends at connection setup. However the RC4 initialization function uses a hash function on the key. I haven't been able to identify the hash function, so had to extract the one from the client to be able to initialize my RC4 routine. (search for "b3 98 b4 9f" and you'll find it)

After that, each byte send/received is just encrypted/decrypted with normal RC4.

As for the actual game packets. Every packet type has a predefined structure, which is handled by a MsgPack / Unpack function. Those functions use a table structure to define the types of fields. The actual msgtype numeric value for a packet can change between 2 revisions of the client, so hard coding those is a bad idea for anyone interested in writing an emulator. It is however fairly easy to extract the structure from the game client. While fooling around with it, I wrote a small program using libbfd to parse the executable and dump a header file with the structure of the packets, enabling me to decode a wireshark capture.

ie. example extract:

struct netFieldI serverMsgWvwCliObjectiveMgr_273[] = {
        NETFIELD_MSGID(0x273, "serverMsgWvwCliObjectiveMgr_273"),
                NETFIELD_BYTE(),
        NETFIELD_END()
};



Since they still have that security issue I referred to a while ago, you don't need any hacks to decode the wireshark. I still haven't heard anything back from them, so I'll probably spill the beans about it one of these days.

Just to help anyone fooling around with it, here are the DH params for the current client (16974) :

const unsigned char p[] = {
0xe7, 0x6f, 0x21, 0x5a, 0x30, 0x4c, 0x56, 0x37, 0x83, 0x1b, 0x17, 0x9c, 0xde, 0x5d, 0xc7, 0x1e,
0x10, 0xec, 0xac, 0xbd, 0xd1, 0x96, 0x72, 0xe3, 0xd3, 0xcc, 0x73, 0x49, 0x0a, 0xfb, 0xb9, 0x97,
0x60, 0x49, 0x02, 0xe4, 0x96, 0xac, 0x4c, 0x8a, 0x39, 0xa7, 0xb2, 0xfa, 0xe4, 0x9d, 0x75, 0x98,
0xc6, 0x81, 0xd1, 0xa1, 0x28, 0x1a, 0x6a, 0x57, 0xb6, 0xf9, 0xa5, 0x1a, 0xf5, 0x11, 0x00, 0x89,
};
const unsigned char b[] = {
0x69, 0xa9, 0x48, 0xfe, 0x6b, 0xe4, 0x4e, 0xd4, 0xc3, 0x85, 0x8e, 0xdf, 0xc6, 0xc9, 0xd4, 0xd1,
0x82, 0x10, 0x9e, 0xce, 0x41, 0xcc, 0x34, 0x36, 0x9f, 0x13, 0xad, 0x37, 0x78, 0x05, 0x08, 0x59,
0xd5, 0x68, 0x2e, 0xe1, 0xf8, 0x34, 0xf3, 0x05, 0x9d, 0x12, 0x85, 0x85, 0x24, 0x7a, 0xa4, 0x69,
0xa9, 0x30, 0x98, 0xd4, 0x21, 0x22, 0xb6, 0xa0, 0x7c, 0x4f, 0xbf, 0x5e, 0x8d, 0x4b, 0x34, 0xe8,
};


g ofcourse is 4.


Another useful tip.. the protocol for this game connection is almost identical to how the GPLv3-licensed CyanWorlds.com Engine (Headspin/Plasma) functions. I'm actually fairly sure, they share a common origin, especially since some of the error messages and constant values of a certain template function are identical to this. The message pack/unpack is also similar, but that one isn't an exact copy.

Friday, December 21, 2012

Portal connection

Portal connection


Time to dig into the specifics of Guild Wars 2's connection...

The portal connection starts off in clear text. The game client will connect, then send a /Sts/Connect command which will include information about the game client.

Example:


P /Sts/Connect STS/1.0
l:252

<Connect>
<ConnType>400</ConnType>
<Address>192.168.1.1</Address>
<ProductType>0</ProductType>
<ProductName>gw2</ProductName>
<AppIndex>1</AppIndex>
<Epoch>999999999</Epoch>
<Program>101</Program>
<Build>1002</Build>
<Process>9999</Process>
</Connect>
P /Auth/StartTls STS/1.0
s:1
l:11

This looks similar to how HTTP requests work. Th
e 'l:' specifies the length of the request and the 's:' is a sequence number. The server will reply and include a reference to this sequence number.

Example:

STS/1.0 400 Success
s:1R
l:46


<Error server="1001" module="2" line="1518"/>

The 's:1R' here, refers to the original client request. The 
/Auth/StartTls command starts the encryption handshake. From here the connection will switch from text to a binary protocol.

The client will begin by sending a buffer starting with:

16 03 03 xx xx

The first byte indicates what kind of packet this is:

14 = ack
15 = server error
16 = auth phase
17 = game packet

The 2 last bytes are the size of the sub buffer that follows.


So looking at the first response the client sends:
 

16 03 03 00 4e
01 00 00 4a 03 03 xx xx xx ...

Shows a subbuffer of size 0x4e (78 bytes). The first byte is the type of the buffer. If you look closely, it's apparent this buffer is also split up (ie. 0x00 0x4a is yet another size indicator of a sub part). I'm going to skip on some details, if you fire up wireshark you can more easily look at what's there.
There's 1 part that is important in this 0x01 sub buffer and that is a client seed that will be used further on. We need to save 0x20 bytes starting at xx as indicated above.

Also important, is that these sub buffers are used in the HMAC calculation, so we need to keep a sha256 hash of them, excluding the 0x14 sub buffer for digest1.

In code:

if (packet[0] != 0x14)
   digest1.process(packet);

digest2.process(packet);
The server will reply using a similar packet format. The first response is:
16 03 03 00 34

So we already know 00 34 is the size of the sub buffer, which contains:

02 00 00 30 03 03 xx xx xx
Like before, sub buffer 0x02 with a size of 0x30. Also similar as the client buffer, is that these first 0x20 bytes are a server seed, followed by some extra parameters which I'm going to skip here.

For what follows now, it's useful to read RFC5054 paragraph 2.2. and have a look at the SRP demo page.

The server follows up with a new large packet:

16 03 03 01 14
0c 00 01 10
   00 80 xx xx xx .....
   00 01 02
   08 yy yy yy yy yy yy yy yy
   00 80 zz zz zz .....
These are actually multi precision integers used for the SRP session server key exchange, more specifically the values N, g, s, B.
N = xx xx xx ...
g = 2
s = yy yy yy yy yy yy yy yy
B = zz zz zz ...
After these the server sends: 
16 03 03 00 04
   0e 00 00 00
The client will in return send it's A key to the server:
16 03 03 00 86
   10 00 00 82
      00 80 xx xx xx xx

 with A = xx xx xx ...


If you look at the SRP demo page (SRP-6a), you can see how all these values fit together. However GuildWars 2 uses a slightly different way to calculate value x, the RFC version uses:

x = H(salt || H(username||':'||password))

whereas Guild Wars 2 uses:

passhash = H(lowercase(unicode_pass||unicode_login))
Then converts this passhash buffer using htonl() and hashes it to form the password field to use in the calculation:
x = H(salt || H(lowercase(username)||':'||H(passhash)))

With all these values, we're able to calculate the shared key S.

The client will now activate encryption by sending the packet:

14 03 03 00 01 01
  
From now on, the buffers will be encrypted, however the shared key S isn't used as-is for the encryption. First a master key is generated from this and then a key expansion function is used to generate a send and a receive key.

But that's for another post...

Wednesday, December 12, 2012

Hmm it's been far too long since I posted something here...


GuildWars 2


So a while ago I got interested in seeing how game security was, more specifically how games these days protect people from snooping the traffic. But I was also interested in what counter measures are present to prevent or detect tampering, since bots/cheats are a big issue these days and can potentially ruin a game.

Since I was planning on playing Guild Wars 2 for a bit, I figured it would be a nice target to  investigate. The investigation was done on the beta client somewhere in march so my memory is already a bit fuzzy on some details. I've waited this long to post about it, since I didn't want to help bot makers, but I doubt it still matters today. A quick search on google will show there's already plenty of hacks out there, so anything posted here won't make much of a difference.


A First look


First thing I noticed was how much information was present in the binary. My initial thought was that this was due to it being a beta client, hence probably contained more debug info. Unfortunately a quick look at a recent binary shows this not to be the case.. there's still lots of useful information present.

For example, even just issuing strings gw2.exe is quite revealing:

!((m_view == VIEW_GAMEPLAY) && !CharClientContext()->GetControlledCharacter())
!iterator.IsValid()
DNot supported in this configuration!
..\..\..\Game\View\Headless\VhdContext.cpp
N@onRendered
..\..\..\Game\View\Default\VdfRender.cpp
metric < arrsize(m_metrics)
..\..\..\Game\View\Default\VdfLoad.cpp
m_state == STATE_LOAD_MANIFEST
m_state == STATE_MODELS_STREAM
m_mapId == mapId
m_state == STATE_LOAD_CONTENT || m_state == STATE_LOAD_MANIFEST || m_state == STATE_SERVER_WAIT
mapContentLoaded
mapDef != NULL
m_state == STATE_LOAD_CONTENT
m_state == STATE_SERVER_WAIT
m_state == STATE_MAP_STREAM

Even today's binary (16247) still shows all this detail. Anyone who has ever reversed code will understand how valuable this is during the reversing process.

The game was written in C++ with RTTI enabled, which is also a great source for information. (See Igor Skochinsky's post Recon 2012: Compiler Internals)

Anyway I'll explain the network protocols used by GuildWars 2 on here, which might help some people that are interested in writing a gw2 server emulator. I'll probably spread this over a couple of posts.

While reversing the client, I also discovered a security issue, which to this day is still present. Therefor I've notified ArenaNet today. I'll refrain from explaining the issue for some time, or until ArenaNet resolves the issue. Whichever comes first.

On to the nitty gritty now.


The connections


GuildWars 2 uses 2 ports for network communication:

- port 6600 : portal connection
- port 6112 : game server connection

Actually, ports 80 and 443 are also required, but those aren't involved in actual gameplay and it's pretty known which protocol is used on those :-)


Portal Connection


The portal connection starts as a clear text protocol, but immediately issues an AUTH TLS command, which initiates a secured connection. The actual data protocol used on this port is XML based. The game uses RapidXML to do the XML parsing.

Example XML piece:
<Reply>
<UserId>99999999-9999-9999-9999-999999999999</UserId>
<UserCenter>1</UserCenter>
<HasPhone>0</HasPhone>
</Reply>

The encryption used on this connection is AES in CBC mode. With a separate session key for incoming and outgoing traffic. An HMAC digest hash is also used, again one for incoming and one for outgoing communications.

The key exchange between the client and server is done via SRP (RFC5054, Secure Remote Password). Since SRP uses Diffie-Hellman, it won't prevent proxying this connection, which would allow people interested in writing cheats to get access to the communication on an external machine, without having to tamper with the actual client binary.

However this doesn't matter much as the game binary I investigated contained no security checks.

The game goes through some key expansion functions to generate the AES & HMAC keys.

NOTE: this is not vulnerable to MITM attacks, as one needs to know the password to be able to proxy the connection.

Game server connection


The connection to the game servers is over an RC4 encrypted connection. Again Diffie-Hellman is used as a key exchange, but in this case the server side packet is precomputed and stored in the game client, ie. the result of ga mod p has been precalculated and is stored in the game binary. Each release uses a new set of keys, so there's no point in trying to find the server key a (would take too long anyway).

The same key is used for transmitting and receiving data, but both paths use a separate state.

From what I heard, this is the same as how it was in GuildWars 1.

The actual data protocol used is a binary protocol with variable sized packets. The client has a MsgPack/Unpack function to (de)serialize the data to/from a C structure.

Again this is similar to how GuildWars 1 worked.


This is a quick overview of how the communication works, I'll go into greater detail in a following post.