Skip to content

Commit 4bb41a1

Browse files
red-001nerzhul
authored andcommittedFeb 18, 2018
Mitigate formspec exploits by verifying that the formspec was shown to the user by the server. (#6878)
This doesn't check the fields in anyway whatsoever so it should only be seen as a way to mitigate exploits, a last line of defense to make it harder to exploit bugs in mods, not as a reason to not do all the usually checks.
1 parent 63bcd33 commit 4bb41a1

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed
 

‎src/network/serverpackethandler.cpp

+28-3
Original file line numberDiff line numberDiff line change
@@ -1470,10 +1470,10 @@ void Server::handleCommand_NodeMetaFields(NetworkPacket* pkt)
14701470

14711471
void Server::handleCommand_InventoryFields(NetworkPacket* pkt)
14721472
{
1473-
std::string formname;
1473+
std::string client_formspec_name;
14741474
u16 num;
14751475

1476-
*pkt >> formname >> num;
1476+
*pkt >> client_formspec_name >> num;
14771477

14781478
StringMap fields;
14791479
for (u16 k = 0; k < num; k++) {
@@ -1501,7 +1501,32 @@ void Server::handleCommand_InventoryFields(NetworkPacket* pkt)
15011501
return;
15021502
}
15031503

1504-
m_script->on_playerReceiveFields(playersao, formname, fields);
1504+
if (client_formspec_name.empty()) { // pass through inventory submits
1505+
m_script->on_playerReceiveFields(playersao, client_formspec_name, fields);
1506+
return;
1507+
}
1508+
1509+
// verify that we displayed the formspec to the user
1510+
const auto peer_state_iterator = m_formspec_state_data.find(pkt->getPeerId());
1511+
if (peer_state_iterator != m_formspec_state_data.end()) {
1512+
const std::string &server_formspec_name = peer_state_iterator->second;
1513+
if (client_formspec_name == server_formspec_name) {
1514+
m_script->on_playerReceiveFields(playersao, client_formspec_name, fields);
1515+
if (fields["quit"] == "true")
1516+
m_formspec_state_data.erase(peer_state_iterator);
1517+
return;
1518+
}
1519+
actionstream << "'" << player->getName()
1520+
<< "' submitted formspec ('" << client_formspec_name
1521+
<< "') but the name of the formspec doesn't match the"
1522+
" expected name ('" << server_formspec_name << "')";
1523+
1524+
} else {
1525+
actionstream << "'" << player->getName()
1526+
<< "' submitted formspec ('" << client_formspec_name
1527+
<< "') but server hasn't sent formspec to client";
1528+
}
1529+
actionstream << ", possible exploitation attempt" << std::endl;
15051530
}
15061531

15071532
void Server::handleCommand_FirstSrp(NetworkPacket* pkt)

‎src/server.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -1571,8 +1571,10 @@ void Server::SendShowFormspecMessage(session_t peer_id, const std::string &forms
15711571
NetworkPacket pkt(TOCLIENT_SHOW_FORMSPEC, 0 , peer_id);
15721572
if (formspec.empty()){
15731573
//the client should close the formspec
1574+
m_formspec_state_data.erase(peer_id);
15741575
pkt.putLongString("");
15751576
} else {
1577+
m_formspec_state_data[peer_id] = formname;
15761578
pkt.putLongString(FORMSPEC_VERSION_STRING + formspec);
15771579
}
15781580
pkt << formname;
@@ -2660,6 +2662,9 @@ void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason)
26602662
++i;
26612663
}
26622664

2665+
// clear formspec info so the next client can't abuse the current state
2666+
m_formspec_state_data.erase(peer_id);
2667+
26632668
RemotePlayer *player = m_env->getPlayer(peer_id);
26642669

26652670
/* Run scripts and remove from environment */

‎src/server.h

+2
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,8 @@ class Server : public con::PeerHandler, public MapEventReceiver,
591591
*/
592592
std::queue<con::PeerChange> m_peer_change_queue;
593593

594+
std::unordered_map<session_t, std::string> m_formspec_state_data;
595+
594596
/*
595597
Random stuff
596598
*/

0 commit comments

Comments
 (0)
Please sign in to comment.