PDA

View Full Version : GMAccountIPList


cybernine186
02-13-2009, 07:32 PM
I am the former Lead GM Voidd from the VZTZ Server and a while back we had issues with some hackers that some how gained access to all GM accounts.

I conducted an investigation and found the most likely source of the issue but with further information a fix for the source could not be issued.

Instead I come up with a way to protect our GM accounts with ip account limiting with account status. Below I have attached a patch for the latest version of eqemu from the SVN.

I think I got all the code from the source correct but if anyone has issues please reply here and I will look to see if I missed any code.

I strongly encourage all server admins to add this patch to prevent someone from hacking your GM accounts. You will need to add the sql statements below and add your GM account id with your ip address in the gm_ips table.


SVN Patch (Tortoise Patch File)
Index: common/database.cpp
================================================== =================
--- common/database.cpp (revision 315)
+++ common/database.cpp (working copy)
@@ -252,8 +252,43 @@
return true;
}
//End Lieka Edit
+
+ bool Database::CheckGMIPs(const char* ip_address, int32 account_id) {
+ char errbuf[MYSQL_ERRMSG_SIZE];
+ char *query = 0;
+ MYSQL_RES *result;
+ MYSQL_ROW row;
+ if (RunQuery(query, MakeAnyLenString(&query, "SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id), errbuf, &result)) {
+ safe_delete_array(query);
+ if (mysql_num_rows(result) == 1) {
+ mysql_free_result(result);
+ return true;
+ } else {
+ mysql_free_result(result);
+ return false;
+ }
+ mysql_free_result(result);

+ } else {
+ safe_delete_array(query);
+ return false;
+ }
+
+ return false;
+}

+bool Database::AddGMIP(char* ip_address, char* name) {
+ char errbuf[MYSQL_ERRMSG_SIZE];
+ char *query = 0;
+
+ if (!RunQuery(query, MakeAnyLenString(&query, "INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name), errbuf)) {
+ safe_delete_array(query);
+ return false;
+ }
+ safe_delete_array(query);
+ return true;
+}
+
sint16 Database::CheckStatus(int32 account_id)
{
char errbuf[MYSQL_ERRMSG_SIZE];
Index: common/database.h
================================================== =================
--- common/database.h (revision 315)
+++ common/database.h (working copy)
@@ -140,6 +140,8 @@
int32 GetCharacterID(const char *name);
bool CheckBannedIPs(const char* loginIP); //Lieka Edit: Check incomming connection against banned IP table.
bool AddBannedIP(char* bannedIP, const char* notes); //Lieka Edit: Add IP address to the Banned_IPs table.
+ bool CheckGMIPs(const char* loginIP, int32 account_id);
+ bool AddGMIP(char* ip_address, char* name);

/*
* Account Related
Index: common/ruletypes.h
================================================== =================
--- common/ruletypes.h (revision 315)
+++ common/ruletypes.h (working copy)
@@ -86,6 +86,7 @@
RULE_BOOL ( World, ClearTempMerchantlist, true) //cavedude: Clears temp merchant items when world boots.
RULE_INT ( World, AccountSessionLimit, -1 ) //Max number of characters allowed on at once from a single account (-1 is disabled)
RULE_INT ( World, ExemptAccountLimitStatus, -1 ) //Min status required to be exempt from multi-session per account limiting (-1 is disabled)
+RULE_BOOL ( World, GMAccountIPList, false) // Voidd: Check ip list against GM Accounts, AntiHack GM Accounts.
RULE_CATEGORY_END()

RULE_CATEGORY( Zone )
Index: world/client.cpp
================================================== =================
--- world/client.cpp (revision 315)
+++ world/client.cpp (working copy)
@@ -1,6 +1,7 @@
#include "../common/debug.h"
#include "../common/EQPacket.h"
#include "../common/EQStreamIntf.h"
+#include "../common/misc.h"
#include <iostream>
using namespace std;
#include <iomanip>
@@ -181,6 +182,14 @@
return false;
}

+ // Voidd: Anti-GM Account hack, Checks source ip against valid GM Account IP Addresses
+ if (RuleB(World, GMAccountIPList) && this->GetAdmin() > 0) {
+ if(!database.CheckGMIPs(long2ip(this->GetIP()).c_str(), this->GetAccountID())) {
+ clog(WORLD__CLIENT,"GM Account not permited from source address %s and accountid %i", long2ip(this->GetIP()).c_str(), this->GetAccountID());
+ eqs->Close();
+ }
+ }
+
if (GetAccountID() == 0 && opcode != OP_SendLoginInfo) {
// Got a packet other than OP_SendLoginInfo when not logged in
clog(WORLD__CLIENT_ERR,"Expecting OP_SendLoginInfo, got %s", OpcodeNames[opcode]);


SQL Rule
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`) VALUES (0, 'World:GMAccountIPList', 'true');

SQL Table Structure
(The name is only for human reference but the account_id must match your GM account with your current ip address otherwise after server select your eq client will crash. You may add multiple entries for 1 gm account id with ip addresses.)
CREATE TABLE IF NOT EXISTS `gm_ips` (
`name` varchar(64) NOT NULL,
`account_id` int(11) NOT NULL,
`ip_address` varchar(15) NOT NULL,
UNIQUE KEY `account_id` (`account_id`,`ip_address`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

trevius
02-15-2009, 08:25 PM
Very nice. I will have to give this a shot when I get some time. Better security is always a good thing and this is a big one :)

Yeormom
02-17-2009, 03:09 PM
Thanks for sharing. I implemented a similar approach without the rules but this is much better for the general public.

cavedude
02-17-2009, 09:13 PM
I tried this on my local server and seems to work fine. I'll go ahead and commit it, however I'm going to make the rule insert false by default so people don't start wondering why their GMs keep crashing ;) Thank you for this, one less thing I need to worry about!

cybernine186
02-17-2009, 11:05 PM
That will be fine caved00d, also on a side note this script has been running for 3+ months now on the VZTZ Server and is currently still being used.