crypt: Modernize and add test. Fix OnNumericMessage 332 issue.

It was possible to trigger null pointer dereference during
OnNumericMessage

The affected versions are 1.7.0+

CVE-2026-82374
This commit is contained in:
RealKindOne authored and Alexey Sokolov committed 2026-09-12 19:08:21 +01:00
1 parent 4b88c64c6e
commit af5cdd0192
2 files changed
+110 -17

No files matched your search

+37 -16
View File
@@ -248,13 +248,19 @@ class CCryptMod : public CModule {
FilterOutgoing(Message); FilterOutgoing(Message);
return CONTINUE; return CONTINUE;
} }
EModRet OnPrivTextMessage(CTextMessage& Message) override {
CNick& Nick = Message.GetNick();
CString sMessage = Message.GetText();
CString sHostmask = Nick.GetHostMask();
EModRet OnPrivMsg(CNick& Nick, CString& sMessage) override {
FilterIncoming(Nick.GetNick(), Nick, sMessage); FilterIncoming(Nick.GetNick(), Nick, sMessage);
Message.SetText(sMessage);
return CONTINUE; return CONTINUE;
} }
EModRet OnPrivNotice(CNick& Nick, CString& sMessage) override { EModRet OnPrivNoticeMessage(CNoticeMessage& Message) override {
CNick& Nick = Message.GetNick();
CString sMessage = Message.GetText();
CString sCommand = sMessage.Token(0); CString sCommand = sMessage.Token(0);
CString sOtherPubKey = sMessage.Token(1); CString sOtherPubKey = sMessage.Token(1);
@@ -305,33 +311,42 @@ class CCryptMod : public CModule {
} }
FilterIncoming(Nick.GetNick(), Nick, sMessage); FilterIncoming(Nick.GetNick(), Nick, sMessage);
Message.SetText(sMessage);
return CONTINUE; return CONTINUE;
} }
EModRet OnPrivAction(CNick& Nick, CString& sMessage) override { EModRet OnPrivActionMessage(CActionMessage& Message) override {
FilterIncoming(Nick.GetNick(), Nick, sMessage); CString sText = Message.GetText();
FilterIncoming(Message.GetNick().GetNick(), Message.GetNick(), sText);
Message.SetText(sText);
return CONTINUE; return CONTINUE;
} }
EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage) override { EModRet OnChanTextMessage(CTextMessage& Message) override {
FilterIncoming(Channel.GetName(), Nick, sMessage); CString sText = Message.GetText();
FilterIncoming(Message.GetTarget(), Message.GetNick(), sText);
Message.SetText(sText);
return CONTINUE; return CONTINUE;
} }
EModRet OnChanNotice(CNick& Nick, CChan& Channel, EModRet OnChanNoticeMessage(CNoticeMessage& Message) override {
CString& sMessage) override { CString sText = Message.GetText();
FilterIncoming(Channel.GetName(), Nick, sMessage); FilterIncoming(Message.GetTarget(), Message.GetNick(), sText);
Message.SetText(sText);
return CONTINUE; return CONTINUE;
} }
EModRet OnChanAction(CNick& Nick, CChan& Channel, EModRet OnChanActionMessage(CActionMessage& Message) override {
CString& sMessage) override { CString sText = Message.GetText();
FilterIncoming(Channel.GetName(), Nick, sMessage); FilterIncoming(Message.GetTarget(), Message.GetNick(), sText);
Message.SetText(sText);
return CONTINUE; return CONTINUE;
} }
EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sMessage) override { EModRet OnTopicMessage(CTopicMessage& Message) override {
FilterIncoming(Channel.GetName(), Nick, sMessage); CString sText = Message.GetText();
FilterIncoming(Message.GetTarget(), Message.GetNick(), sText);
Message.SetText(sText);
return CONTINUE; return CONTINUE;
} }
@@ -342,10 +357,16 @@ class CCryptMod : public CModule {
CChan* pChan = GetNetwork()->FindChan(Message.GetParam(1)); CChan* pChan = GetNetwork()->FindChan(Message.GetParam(1));
if (pChan) { if (pChan) {
CNick* Nick = pChan->FindNick(Message.GetParam(0)); CNick* pNick = pChan->FindNick(Message.GetParam(0));
CString sTopic = Message.GetParam(2); CString sTopic = Message.GetParam(2);
FilterIncoming(pChan->GetName(), *Nick, sTopic); if (pNick) {
FilterIncoming(pChan->GetName(), *pNick, sTopic);
} else {
// 332 can arrive before ZNC has this nick in the nicklist.
CNick TempNick(Message.GetParam(0));
FilterIncoming(pChan->GetName(), TempNick, sTopic);
}
Message.SetParam(2, sTopic); Message.SetParam(2, sTopic);
} }
+73 -1
View File
@@ -138,7 +138,11 @@ TEST_F(ZNCTest, WatchModule) {
":*spaces!watch@znc.in PRIVMSG nick :<n:#znc> SOMETHING word1 word2 SOMETHING"); ":*spaces!watch@znc.in PRIVMSG nick :<n:#znc> SOMETHING word1 word2 SOMETHING");
} }
TEST_F(ZNCTest, ModuleCrypt) { TEST_F(ZNCTest, CryptModule) {
#ifndef HAVE_LIBSSL
GTEST_SKIP() << "SSL is disabled";
#endif
QFile conf(m_dir.path() + "/configs/znc.conf"); QFile conf(m_dir.path() + "/configs/znc.conf");
ASSERT_TRUE(conf.open(QIODevice::Append | QIODevice::Text)); ASSERT_TRUE(conf.open(QIODevice::Append | QIODevice::Text));
QTextStream(&conf) << "ServerThrottle = 1\n"; QTextStream(&conf) << "ServerThrottle = 1\n";
@@ -184,6 +188,8 @@ TEST_F(ZNCTest, ModuleCrypt) {
QByteArray key2(""); QByteArray key2("");
client2.ReadUntilAndGet("\002user\017: ", key2); client2.ReadUntilAndGet("\002user\017: ", key2);
ASSERT_EQ(key1.mid(9), key2.mid(8)); ASSERT_EQ(key1.mid(9), key2.mid(8));
// OnPrivTextMessage
client1.Write("CAP REQ :echo-message"); client1.Write("CAP REQ :echo-message");
client1.Write("PRIVMSG .nick2 :Hello"); client1.Write("PRIVMSG .nick2 :Hello");
QByteArray secretmsg; QByteArray secretmsg;
@@ -191,6 +197,72 @@ TEST_F(ZNCTest, ModuleCrypt) {
ircd2.Write(":user!user@user/test " + secretmsg); ircd2.Write(":user!user@user/test " + secretmsg);
client2.ReadUntil("Hello"); client2.ReadUntil("Hello");
client1.ReadUntil(secretmsg); // by echo-message client1.ReadUntil(secretmsg); // by echo-message
client1.Write("PRIVMSG *crypt :SetNickPrefix .");
client1.ReadUntil("Setting Nick Prefix to .");
client2.Write("PRIVMSG *crypt :SetNickPrefix .");
client2.ReadUntil("Setting Nick Prefix to .");
// OnPrivNoticeMessage
client1.Write("NOTICE .nick2 :secret notice");
QByteArray noticeMsg;
ircd1.ReadUntilAndGet("NOTICE nick2 :+OK ", noticeMsg);
ircd2.Write(":user!user@user/test " + noticeMsg);
QByteArray noticeLine;
client2.ReadUntilAndGet("NOTICE nick2 :", noticeLine);
QByteArray noticemessage = noticeLine.mid(noticeLine.lastIndexOf(':') + 1);
EXPECT_EQ(noticemessage, "secret notice");
// OnUserActionMessage
client1.Write("PRIVMSG .nick2 :\001ACTION waves\001");
QByteArray actionMsg;
ircd1.ReadUntilAndGet("PRIVMSG nick2 :\001ACTION +OK ", actionMsg);
ircd2.Write(":user!user@user/test " + actionMsg);
QByteArray actionLine;
client2.ReadUntilAndGet("PRIVMSG nick2 :", actionLine);
QByteArray actionMessage = actionLine.mid(actionLine.lastIndexOf(':') + 1);
EXPECT_EQ(actionMessage, "\001ACTION waves\001");
client1.Write("JOIN #test");
client2.Write("JOIN #test");
QByteArray chanKey = "channelKey123";
client1.Write(QByteArray("PRIVMSG *crypt :SetKey #test ") + chanKey);
client1.ReadUntil(QByteArray("Set encryption key for [#test] to [") +
chanKey + "]");
client2.Write(QByteArray("PRIVMSG *crypt :SetKey #test ") + chanKey);
client2.ReadUntil(QByteArray("Set encryption key for [#test] to [") +
chanKey + "]");
// OnChanTextMessage
client1.Write("PRIVMSG #test :channel secret");
QByteArray chanMsg;
ircd1.ReadUntilAndGet("PRIVMSG #test :+OK ", chanMsg);
ircd2.Write(":user!user@user/test " + chanMsg);
client2.ReadUntil("channel secret");
// OnChanNoticeMessage
client1.Write("NOTICE #test :chan notice");
QByteArray chanNoticeMsg;
ircd1.ReadUntilAndGet("NOTICE #test :+OK ", chanNoticeMsg);
ircd2.Write(":user!user@user/test " + chanNoticeMsg);
client2.ReadUntil("chan notice");
// OnChanActionMessage
client1.Write("PRIVMSG #test :\001ACTION dances\001");
QByteArray chanActionMsg;
ircd1.ReadUntilAndGet("PRIVMSG #test :\001ACTION +OK ", chanActionMsg);
ircd2.Write(":user!user@user/test " + chanActionMsg);
client2.ReadUntil("dances");
// OnTopicMessage / OnNumericMessage
client1.Write("TOPIC #test :new chan topic");
QByteArray chanTopicEncrypted;
ircd1.ReadUntilAndGet("TOPIC #test :+OK ", chanTopicEncrypted);
chanTopicEncrypted = "+OK " + chanTopicEncrypted.mid(
chanTopicEncrypted.lastIndexOf(' ') + 1);
ircd2.Write(":ircd2 332 nick2 #test :" + chanTopicEncrypted);
client2.ReadUntil("new chan topic");
} }
TEST_F(ZNCTest, AutoAttachModule) { TEST_F(ZNCTest, AutoAttachModule) {