[crossfire] large numbers of parties causes weirdness

Brendan Lally b.t.lally at warwick.ac.uk
Wed Apr 27 22:55:42 CDT 2005


Well, there was a mention of a security audit a while back, here's a result 
from it, and even a sample exploit code :)

The party that a person is a member of is stored in a 'partyid' in the player 
struct. This is an sint16. each party gets an sint16 as the party number, and 
The following code, run as a script by a player is enough to cause breakages 
in the party system that crossfire uses.

#!/bin/bash
for i in $(seq 1 1 65536); 
do
echo "issue 1 1 party form $i"
sleep 0.05
done

there are three issues.
1) when it gets above about 1800 it causes clients to crash when doing party 
list. 

2) when it gets above 32768 the parties stop being functional (they can be 
joined, but aren't considered to be joined by any aspect of the game code).

3) when it gets above 65535, the partyid (which is stored as a sint16) will 
wrap around, allowing parties to be joined by the one 65536 places after them 
on the list. This avoids the password check. This would allow, for example 
someone to run that script and then join the party that corresponds to party 
dm on metalforge, without needing the password.

with regards to the third one a possible solution is (this compiles, haven't 
tested yet, but it should work.).


Index: server/c_party.c
===================================================================
RCS file: /cvsroot/crossfire/crossfire/server/c_party.c,v
retrieving revision 1.10
diff -C5 -r1.10 c_party.c
*** server/c_party.c    25 Dec 2004 18:09:29 -0000      1.10
--- server/c_party.c    28 Apr 2005 03:19:30 -0000
***************
*** 452,461 ****
--- 452,466 ----
                "You are already a member of party: %s"
                    ,tmpparty->partyname);
            return 1;
          }
          else {
+         if(find_party_struct(tmpparty->partyid)!=tmpparty) {
+             new_draw_info_format(NDI_UNIQUE, 0, op,
+               "ERROR Detected, party numbers are inconsistant, can't join 
party %s",params);
+             return 1;
+         }
            if(tmpparty->passwd[0] == '\0') {
              new_draw_info_format(NDI_UNIQUE, 0, op,
                        "You have joined party: %s",tmpparty->partyname);
              op->contr->party_number = tmpparty->partyid;
              snprintf( buf, MAX_BUF, "%s joins party %s", op->name, 
tmpparty->partyname );


other options include extending pl->party_number and partylist->partyid to be 
sint32's (that will make the exploit take longer to occur, but doesn't fix 
the underlying problem). This also adresses point 2.


Index: include/player.h
===================================================================
RCS file: /cvsroot/crossfire/crossfire/include/player.h,v
retrieving revision 1.37
diff -C5 -r1.37 player.h
*** include/player.h    15 Aug 2004 17:03:07 -0000      1.37
--- include/player.h    28 Apr 2005 03:33:18 -0000
***************
*** 170,181 ****
      time_t    last_save_time;
  #endif /* SAVE_INTERVAL */
  #ifdef AUTOSAVE
      uint32    last_save_tick;
  #endif
!     sint16    party_number;       /* Party number this player is part of */
!     sint16    party_number_to_join; /* used when player wants to join a 
party */
                                    /* but we will have to get password first 
*/
                                    /* so we have to remember which party to 
*/
                                    /* join */
      char    search_str[MAX_BUF];    /* Item we are looking for */
      sint16  encumbrance;          /* How much our player is encumbered  */
--- 170,181 ----
      time_t    last_save_time;
  #endif /* SAVE_INTERVAL */
  #ifdef AUTOSAVE
      uint32    last_save_tick;
  #endif
!     sint32    party_number;       /* Party number this player is part of */
!     sint32    party_number_to_join; /* used when player wants to join a 
party */
                                    /* but we will have to get password first 
*/
                                    /* so we have to remember which party to 
*/
                                    /* join */
      char    search_str[MAX_BUF];    /* Item we are looking for */
      sint16  encumbrance;          /* How much our player is encumbered  */
***************
*** 190,200 ****
  } player;


  /* not really the player, but tied pretty closely */
  typedef struct party_struct {
!     sint16 partyid;
      char * partyleader;
      char passwd[9];
      struct party_struct *next;
      char *partyname;

--- 190,200 ----
  } player;


  /* not really the player, but tied pretty closely */
  typedef struct party_struct {
!     sint32 partyid;
      char * partyleader;
      char passwd[9];
      struct party_struct *next;
      char *partyname;


This still doesn't deal with point 1 though, which may be more an issue with 
the network code.

possible things to do;
limit the number of parties a player can create (but without party removal 
that may be considered bad).

limit the global number of parties (that could mean the list could be spammed 
stopping party formation).

have party removal (harder to implement, though possible), and autoremove 
parties that no longer have active members (but then if they all log off, 
their party will go).

have a party player counter, which only changes when the party a player is a 
member of does, use this for party removal, and save the current party in the 
save file (but then there are abuses involving parties that gain passwords, 
or newly formed parties after a server reset). 

    
    


More information about the crossfire mailing list