<<< Date Index >>>     <<< Thread Index >>>

Socket unreacheable in the Lithtech engine (new protocol)



#######################################################################

                             Luigi Auriemma

Application:  Lithtech engine (new network protocol)
              http://www.lithtech.com
Games:        Contract Jack                                      <= 1.1
              No one lives forever 2                             <= 1.3
              Tron 2.0                                         <= 1.042
              ... others?
Platforms:    Windows and Mac
Bug:          socket unreacheable
Exploitation: remote, versus server
Date:         13 December 2004
Author:       Luigi Auriemma
              e-mail: aluigi@xxxxxxxxxxxxx
              web:    http://aluigi.altervista.org


#######################################################################


1) Introduction
2) Bug
3) The Code
4) Fix


#######################################################################

===============
1) Introduction
===============


The Lithtech engine is a game engine used by many games.
Some of the latest games released and based on this engine use a
network protocol different than all the others (probably they use a new
version of the engine but naturally I don't know all these details).

Just these latest games (all developed by Monolith) are those affected
by the bug I'm going to describe: Contract Jack (Nov 2003), No one
lives forever 2 (Oct 2002) and Tron 2.0 (Aug 2003), but possibly others
too.


#######################################################################

======
2) Bug
======


The new network protocol used by the Lithtech engine is composed by a
loop used to handle all the UDP packets received.

A select() function with a time out of 30 seconds searchs for new data
into the socket's queue. If data is received or the socket goes in
time out, a recvfrom() is called and its return value is checked to
know if has happened an error.
If there is a socket error, the game calls WSAGetLastError() to catch
the error code and returns reaching a main check that is made ever
before the usual select() function.
This so-called "main check" simply controls that the error returned by
WSAGetLastError() (and stored in a specific variable) is "Operation
would block" (10035, the only type of error accepted to continue the
listening loop).

If an attacker sends an UDP packet of zero bytes, recvfrom() returns
this length and an instruction checks just if it is equal than zero. In
this case the code flow returns to the "main check" that controls the
error code (not set, so equal to zero) and since it is not 10035 exits
from the loop that handles the socket's data.

After that, the server will be no longer able to receive packets
because the loop is completely dead.

A similar problem happens if an attacker sends an UDP packet with a
size major/equal than 8193 bytes (max data read by recvfrom()) and
minor/equal than 12280 (otherwise select() doesn't catch it).
The "main check" will fail as before because the error code will be
different than 10035 (in fact it will be 10040, "Message too long").


#######################################################################

===========
3) The Code
===========


http://aluigi.altervista.org/poc/lithsock.zip


#######################################################################

======
4) Fix
======


No fix.
Just some weeks ago I released an advisory about another bug in the
Lithtech engine and ignored by the developers, so is useless to try to
recontact them again.

However I have found a work-around to avoid the problem and I have
written an universal autopatcher that can fix both lithtech.exe (the
server launched from the game) and server.dll (the dedicated server):

  http://aluigi.altervista.org/patches/lithsockfix.zip


#######################################################################


--- 
Luigi Auriemma
http://aluigi.altervista.org