Coordinated Disclosure Timeline
- 2026-03-17: Security at signal.org was notified about the vulnerability
- 2026-03-18: The fix was committed
- 2026-03-19: Signal team acknowledged
- 2026-03-19: The version 8.3 was released on GitHub and pushed to the Apple App Store
- 2026-03-19: Signal team decided not to issue CVE
Summary
Signal for iOS v8.2 is affected by a vulnerability (GHSL-2026-095) where an attacker can exploit an improper authorization check within the Admin Delete message handler, leading to unauthorized message deletion.
Project
Signal-iOS
Tested Version
Details
Admin Delete Authorization Check After Destructive Action (GHSL-2026-095)
Admin Delete is a new group moderation feature intended to allow group administrators to delete unwanted messages. While at the moment of reporting it is not available in the UI, the BuildFlags.AdminDelete.receive flag is already enabled and handled in the publicly available v8.2 Signal app. Differently from Signal for Android it is not a server flag and the functionality cannot be turned off remotely without users updating their Signal. A malicious custom (or modified) client can craft and send a custom message packet with an Admin Delete request.
In AdminDeleteManager.tryToAdminDeleteMessage(), the destructive action TSMessage.remotelyDeleteMessage() [1] is called BEFORE the admin authorization check in insertAdminDelete() [2]. The admin check groupModel.membership.isFullMemberAndAdministrator(deleteAuthor) [3] occurs inside insertAdminDelete(). If the sender is NOT an admin, insertAdminDelete throws .invalidDelete, but remotelyDeleteMessage() has already called markMessageAsRemotelyDeleted() (lines 388/404/424), which destructively removes the message content via updateWithRemotelyDeletedAndRemoveRenderableContent. Since all these operations occur within the same DBWriteTransaction and the thrown error is caught by the caller at MessageReceiver.swift (which logs and returns nil without causing a transaction rollback), the destructive deletion is committed to the database.
public func tryToAdminDeleteMessage(
...
) throws(TSMessage.RemoteDeleteError) {
guard SDS.fitsInInt64(sentAtTimestamp) else {
owsFailDebug("Unable to delete a message with invalid sentAtTimestamp: \(sentAtTimestamp)")
throw .invalidDelete
}
if
let threadUniqueId, let messageToDelete = InteractionFinder.findMessage(
...
)
{
let allowDeleteTimeframe = RemoteConfig.current.adminDeleteMaxAgeInSeconds + .day
let latestMessage = try TSMessage.remotelyDeleteMessage( // [1]
messageToDelete,
deleteAuthorAci: deleteAuthorAci,
allowedDeleteTimeframeSeconds: allowDeleteTimeframe,
serverTimestamp: serverTimestamp,
transaction: transaction,
)
return try insertAdminDelete( // [2]
groupThread: groupThread,
interactionId: latestMessage.sqliteRowId!,
deleteAuthor: deleteAuthorAci,
tx: transaction,
)
} else {
throw .deletedMessageMissing
}
}
private func insertAdminDelete(
...
) throws(TSMessage.RemoteDeleteError) {
guard
let groupModel = groupThread.groupModel as? TSGroupModelV2,
groupModel.membership.isFullMemberAndAdministrator(deleteAuthor) // [3]
else {
logger.error("Failed to process admin delete for non-admin")
throw .invalidDelete
}
...
}
Attack Scenario:
- Attacker is a regular (non-admin) member of a Signal group.
- Attacker crafts and sends a protobuf message with an
adminDeletefield (SSKProtoDataMessageAdminDelete) containing:targetAuthorAciBinary: The ACI of any group member whose message they want to deletetargetSentTimestamp: The timestamp of the target message
- The receiving client at MessageReceiver.swift processes the admin delete.
preprocessDataMessageverifies the attacker is a full group member — this passes.tryToAdminDeleteMessageis called withdeleteAuthorAci: envelope.sourceAci(the attacker’s cryptographically authenticated ACI).InteractionFinder.findMessagelocates the target message.TSMessage.remotelyDeleteMessageis called, which checks time windows and then callsmarkMessageAsRemotelyDeleted, destructively removing the message content and all attachments from the local database.insertAdminDeletechecksisFullMemberAndAdministrator(deleteAuthor)— this fails because the attacker is not an admin — throws.invalidDelete.- The error is caught. The transaction commits with the message already deleted.
Impact
A non-admin group member can delete any other member’s messages (including admin messages) from all group members’ devices, as long as the target message is within the adminDeleteMaxAgeInSeconds + 1 day time window (default ~2 days). This bypasses the intended admin-only restriction on admin delete functionality.
CWEs
- CWE-863: “Incorrect Authorization”
Credit
This issue was discovered with the GitHub Security Lab Taskflow Agent and verified by GHSL team member @JarLob (Jaroslav Lobačevski).
Contact
You can contact the GHSL team at securitylab@github.com, please include a reference to GHSL-2026-095 in any communication regarding this issue.