Pet Attribute Overflow Exploit - Battle of the Immortals (BOI) / War of the Immortals (WOI)
What is it
Integer overflow bug in the server-side handler for packet 0x4466 (MsgPet_C2S_AddPoint) — the packet the client sends when you distribute pet attribute points. You can allocate 63k+ to every stat while the server thinks you spent zero points.
How it works
When you distribute pet attribute points in-game, the client sends 5 uint16 values to the server (STR, CON, DEX, INT, SPR). The server:
Sums all 5 values
Checks if sum ≤ available points
If yes: applies each stat individually with the full value you sent
If no: rejects
Seems fine, right? The catch is in how the sum gets stored. Whoever wrote this handler stored the sum as a 16-bit word (max 65535). But 5 × 65535 = 327675 — easily overflows a word.
When the real sum exceeds 65535, the word wraps around to zero. The server then compares 0 ≤ availablePoints → approves. But it still writes each stat with the full uint16 value you sent.
Code:
Pseudocode of the vulnerable handler:
sum = str + con + dex + int + spr // e.g. 63536+63536+63536+63536+8000 = 262144
mov word ptr [sum], ax // ⚠️ truncates to 16-bit → 0x0000
...
movzx ecx, word ptr [sum] // loads zero
cmp ecx, [availablePoints] // 0 ≤ anything → ALWAYS PASSES
Real sum: 4×63536 + 8000 = 262144 (0x40000)
Truncated to word: 0x0000
Server sees: "0 points used, approved"
Server applies: STR=63536, CON=63536, DEX=63536, INT=63536, SPR=8000
You just got a quarter million stat points. For free.
Proof of Concept Tool
Attached is a C++/CLI tool that automates the exploit:
Opens game.exe with read/write access
Reads your owned pet list and shows all pets with their current stats
Writes the exploit values directly into the pet's distribution fields in client memory
Forces the "Done" button so you can send without having real points
Usage:
Summon your pet in-game first
Run PetExploit.exe as Administrator
Select game.exe, click Attach
Pick your pet from the list, click Exploit
Open pet attribute window in-game, click Done
The client sends the manipulated values via packet 0x4466, the server's broken validation approves it, and the stats stick. No packet injection needed — the game client does the sending for you.
Affected
Any server running Battle of the Immortals (BOI) or War of the Immortals (WOI) server binaries that still have the original 0x4466 handler. If the server uses the unmodified handler, it's vulnerable. The bug is in the server binary, not the client — so every player on that server can do this.
Why it works in depth
The root cause is a type width mismatch in the validation logic. The individual stat values are 16-bit (uint16). Five of them summed need at least 18 bits (log2(327675) ≈ 18.3). But the developer used a word-sized temporary for the sum — effectively validating with `(sum & 0xFFFF)` instead of the actual sum.
This is a classic CWE-190 (Integer Overflow/Wraparound) combined with a TOCTOU-style logic gap: the validation uses the truncated sum, but the application uses the full values. The three `movzx` instructions in the handler confirm it — they load the sum assuming it's a word that needs zero-extending to dword, which is correct for a word, but wrong when that word should have been a dword all along.
-- ----------------------------
-- Procedure structure for petbugban
-- ----------------------------
DROP PROCEDURE IF EXISTS `petbugban`;
DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `petbugban`(IN PBDID INTEGER)
BEGIN
DECLARE PBFIND BOOL;
DECLARE PBSEARCH BOOL;
DECLARE PBRID INT;
DECLARE PBAID INT;
DECLARE PBUID INT;
DECLARE PET_VIT_LIMIT INT DEFAULT 1161527296;
DECLARE PET_INT_LIMIT INT DEFAULT 1161527296;
DECLARE PET_STR_LIMIT INT DEFAULT 1161527296;
DECLARE PET_SPR_LIMIT INT DEFAULT 1161527296;
DECLARE PET_DEX_LIMIT INT DEFAULT 1161527296;
SET PBFIND = FALSE;
SET PBSEARCH = FALSE;
BLOCK1: BEGIN
DECLARE cursor1 CURSOR FOR SELECT RoleID,AccountID FROM basetab_sg WHERE AccountID = PBDID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET PBFIND = TRUE;
OPEN cursor1; LOOP1: LOOP FETCH cursor1 INTO PBRID, PBAID;
IF PBFIND = TRUE THEN LEAVE LOOP1; END IF;
BLOCK2: BEGIN
DECLARE PBRESULT BOOL;
DECLARE cursor2 CURSOR FOR SELECT RoleID,CASE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+66,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+64,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+62,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+60,2)),16,10) > PET_VIT_LIMIT THEN TRUE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+106,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+104,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+102,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+100,2)),16,10) > PET_INT_LIMIT THEN TRUE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+146,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+144,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+142,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+140,2)),16,10) > PET_STR_LIMIT THEN TRUE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+186,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+184,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+182,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+180,2)),16,10) > PET_SPR_LIMIT THEN TRUE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+226,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+224,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+222,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+220,2)),16,10) > PET_DEX_LIMIT THEN TRUE ELSE FALSE END FROM pettab_sg WHERE RoleID = PBRID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET PBSEARCH = TRUE;
OPEN cursor2; LOOP2: LOOP FETCH cursor2 INTO PBRID,PBRESULT;
IF PBSEARCH = TRUE THEN LEAVE LOOP2; END IF;
SELECT COUNT(*) into PBUID FROM forbid where userid = PBAID;
IF PBUID = 0 THEN
IF PBRESULT = 1 THEN
call addForbid ( PBAID , 1, 2147483647, 'Petbug', 1);
DELETE FROM pettab_sg where RoleID = PBRID;
END IF;
END IF;
END LOOP LOOP2;
END BLOCK2;
END LOOP LOOP1;
END BLOCK1;
END
;;
DELIMITER ;
-- ----------------------------
-- Procedure structure for recordoffline
-- ----------------------------
DROP PROCEDURE IF EXISTS `recordoffline`;
DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `recordoffline`(in uid1 INTEGER, in aid1 INTEGER, inout zoneid1 INTEGER, inout zonelocalid1 INTEGER, inout overwrite1 INTEGER)
BEGIN
DECLARE rowcount INTEGER;
START TRANSACTION;
UPDATE point SET zoneid = NULL, zonelocalid = NULL WHERE uid = uid1 AND aid = aid1 AND zoneid = zoneid1;
DELETE FROM online WHERE ID = uid1;
SET rowcount = ROW_COUNT();
IF overwrite1 = rowcount THEN
SELECT zoneid, zonelocalid INTO zoneid1, zonelocalid1 FROM point WHERE uid = uid1 AND aid = aid1;
END IF;
call petbugban(uid1);
COMMIT;
END
;;
DELIMITER ;
-- ----------------------------
-- Procedure structure for petbugban
-- ----------------------------
DROP PROCEDURE IF EXISTS `petbugban`;
DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `petbugban`(IN PBDID INTEGER)
BEGIN
DECLARE PBFIND BOOL;
DECLARE PBSEARCH BOOL;
DECLARE PBRID INT;
DECLARE PBAID INT;
DECLARE PBUID INT;
DECLARE PET_VIT_LIMIT INT DEFAULT 1161527296;
DECLARE PET_INT_LIMIT INT DEFAULT 1161527296;
DECLARE PET_STR_LIMIT INT DEFAULT 1161527296;
DECLARE PET_SPR_LIMIT INT DEFAULT 1161527296;
DECLARE PET_DEX_LIMIT INT DEFAULT 1161527296;
SET PBFIND = FALSE;
SET PBSEARCH = FALSE;
BLOCK1: BEGIN
DECLARE cursor1 CURSOR FOR SELECT RoleID,AccountID FROM basetab_sg WHERE AccountID = PBDID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET PBFIND = TRUE;
OPEN cursor1; LOOP1: LOOP FETCH cursor1 INTO PBRID, PBAID;
IF PBFIND = TRUE THEN LEAVE LOOP1; END IF;
BLOCK2: BEGIN
DECLARE PBRESULT BOOL;
DECLARE cursor2 CURSOR FOR SELECT RoleID,CASE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+66,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+64,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+62,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+60,2)),16,10) > PET_VIT_LIMIT THEN TRUE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+106,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+104,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+102,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+100,2)),16,10) > PET_INT_LIMIT THEN TRUE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+146,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+144,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+142,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+140,2)),16,10) > PET_STR_LIMIT THEN TRUE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+186,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+184,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+182,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+180,2)),16,10) > PET_SPR_LIMIT THEN TRUE WHEN CONV(CONCAT(SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+226,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+224,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+222,2),SUBSTR(hex(PetInfo),INSTR(SUBSTR(hex(PetInfo),13), "0001")+220,2)),16,10) > PET_DEX_LIMIT THEN TRUE ELSE FALSE END FROM pettab_sg WHERE RoleID = PBRID;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET PBSEARCH = TRUE;
OPEN cursor2; LOOP2: LOOP FETCH cursor2 INTO PBRID,PBRESULT;
IF PBSEARCH = TRUE THEN LEAVE LOOP2; END IF;
SELECT COUNT(*) into PBUID FROM forbid where userid = PBAID;
IF PBUID = 0 THEN
IF PBRESULT = 1 THEN
call addForbid ( PBAID , 1, 2147483647, 'Petbug', 1);
DELETE FROM pettab_sg where RoleID = PBRID;
END IF;
END IF;
END LOOP LOOP2;
END BLOCK2;
END LOOP LOOP1;
END BLOCK1;
END
;;
DELIMITER ;
-- ----------------------------
-- Procedure structure for recordoffline
-- ----------------------------
DROP PROCEDURE IF EXISTS `recordoffline`;
DELIMITER ;;
CREATE DEFINER=`root`@`localhost` PROCEDURE `recordoffline`(in uid1 INTEGER, in aid1 INTEGER, inout zoneid1 INTEGER, inout zonelocalid1 INTEGER, inout overwrite1 INTEGER)
BEGIN
DECLARE rowcount INTEGER;
START TRANSACTION;
UPDATE point SET zoneid = NULL, zonelocalid = NULL WHERE uid = uid1 AND aid = aid1 AND zoneid = zoneid1;
DELETE FROM online WHERE ID = uid1;
SET rowcount = ROW_COUNT();
IF overwrite1 = rowcount THEN
SELECT zoneid, zonelocalid INTO zoneid1, zonelocalid1 FROM point WHERE uid = uid1 AND aid = aid1;
END IF;
call petbugban(uid1);
COMMIT;
END
;;
DELIMITER ;
your script only runs on logout. dude can use the bugged pet, wreck the whole server, throw the pet away before leaving, and then what is your “anti exploit” scanning? memories?
a real fix is inside the 0x4466 handler, blocking the packet before the stats are applied.
that db script is just cleanup after the crime scene.
you didn’t fix the bug, you put a sleeping guard at the door.
-----------------------
“fix your tool” bro your pc is missing basic microsoft runtime files 💀
i’ll make a release build later so even museum pcs can open it.
Sure, you can disassemble to do a real fix but you'll miss out the opportunity to collect who are those users abusing the exploits all day.
If I'm running the server, I'd prefer to leave an intended exploit, just watch and catch those cheaters and mass ban their ip/mac/hwid altogether, not just simply patch the exploit and be happy.
By just patching it, they can all be trying to pet hack or attempt other hacks eventually without restriction, and you will never know who's doing what.
That procedure is a fix, simple and good enough to read addpoint directly. You can put it anywhere. Trigger, event, login, logout, mail whatever, anyhow you like.
Of course, there are other ways to detect. Like a packet interceptor or db reader that read periodically from db every minute scanning through all pets.
Anyway, I would like to think that this research is of little helpfulness, especially if you will only drop the tool to exploit instead of the tutorial to fix it via disassembly on the line.exe
Yeah, some weirdass DLL needed for hacks.
Amazing research that nothing works
Sure, you can disassemble to do a real fix but you'll miss out the opportunity to collect who are those users abusing the exploits all day.
If I'm running the server, I'd prefer to leave an intended exploit, just watch and catch those cheaters and mass ban their ip/mac/hwid altogether, not just simply patch the exploit and be happy.
By just patching it, they can all be trying to pet hack or attempt other hacks eventually without restriction, and you will never know who's doing what.
That procedure is a fix, simple and good enough to read addpoint directly. You can put it anywhere. Trigger, event, login, logout, mail whatever, anyhow you like.
Of course, there are other ways to detect. Like a packet interceptor or db reader that read periodically from db every minute scanning through all pets.
Anyway, I would like to think that this research is of little helpfulness, especially if you will only drop the tool to exploit instead of the tutorial to fix it via disassembly on the line.exe
Yeah, some weirdass DLL needed for hacks.
Amazing research that nothing works
i dont have woi files, so i have made only for BOI 4th anniversary version, for WOI you need use cheat engine, dont be lazy and use ur brain.exe
WTT BoI Oracle For WOI Destruction Acc or Good Pet 07/06/2013 - Battle of the Immortals - 3 Replies WTT BoI Oracle Acc For WOI Destruction Acc or Good Pet PM
Name:QiWi
LvL:149 max 150
Exalted LvL:43
All Gem LvL:6
Power:48883
OSJ LV:11 , Chalice +13
Have 3x Pet Golden Gaint,Saggitaurus 3G and Gandalf The Midget all in +12 Fortyf and very good Pet Skill And ETC
WTT WOI (Gaia) ACC for WOI (Valhalla) ACC/ZEN/COINS 01/19/2013 - Trading - 2 Replies My Offer:
Name: CaniiiBal
Class: Ranger
Gender: Male
LV. : 98
Server : Gaia
Equipment: LvL 90 Soul Gear 7-8+
Gems: Rock of Tyr Lv4, Diamond of Frigga Lv4, and Gem of Hel Lv2, 4xGem of Hel Lv1, other gems lv3 full gemed
Nobility: Baronet
WOI Genesis 1st WOI pserver 06/14/2012 - Battle of the Immortals - 52 Replies Tell your friends that the 1st WOI Private Server which is x100 Rates is soon to be opened to public!
Play together with your friends with the current updates!
Enchantress - Screenshot game play
http://i.imgur.com/92e10l.jpg
http://i.imgur.com/VwK68l.jpg
http://i.imgur.com/ElllGl.jpg
http://i.imgur.com/NBVqMl.jpg
[Help]Change Sub attribute and ini attribute to Main attribute 11/28/2009 - EO PServer Hosting - 0 Replies I need some help to chage compose Sub attribute and ini attribute to main attribute..
can someone help me??
http://img137.imageshack.us/img137/3025/51830781. jpg