From e7171d64dafd7a1d5e5d73cbe256505f2b609c33 Mon Sep 17 00:00:00 2001 From: Christophe De La Fuente Date: Thu, 22 Sep 2022 15:07:28 +0200 Subject: [PATCH 1/3] Fix NTLMv2 hash when username contains non-ASCI characters --- lib/net/ntlm.rb | 13 ++++++++++++- spec/lib/net/ntlm_spec.rb | 8 ++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/net/ntlm.rb b/lib/net/ntlm.rb index 09c1262..e9c23bf 100644 --- a/lib/net/ntlm.rb +++ b/lib/net/ntlm.rb @@ -161,7 +161,18 @@ def ntlmv2_hash(user, password, target, opt={}) else ntlmhash = ntlm_hash(password, opt) end - userdomain = user.upcase + target + + if opt[:unicode] + # Uppercase operation on username containing non-ASCI characters + # after behing unicode encoded with `EncodeUtil.encode_utf16le` + # doesn't play well. Upcase should be done before encoding. + user_upcase = EncodeUtil.decode_utf16le(user).upcase + user_upcase = EncodeUtil.encode_utf16le(user_upcase) + else + user_upcase = user.upcase + end + userdomain = user_upcase + target + unless opt[:unicode] userdomain = EncodeUtil.encode_utf16le(userdomain) end diff --git a/spec/lib/net/ntlm_spec.rb b/spec/lib/net/ntlm_spec.rb index 00a3d9a..247c70f 100644 --- a/spec/lib/net/ntlm_spec.rb +++ b/spec/lib/net/ntlm_spec.rb @@ -59,6 +59,14 @@ end end + context 'when the username contains non-ASCI characters' do + let(:user) { 'юзер' } + + it 'should return the correct ntlmv2 hash' do + expect(Net::NTLM::ntlmv2_hash(user, passwd, domain)).to eq(["ba3d357a20233dfc432b727537272bab"].pack("H*")) + end + end + it 'should generate an lm_response' do expect(Net::NTLM::lm_response( { From 11ef238cbf68115cec5296a97a5700b855d2940d Mon Sep 17 00:00:00 2001 From: Christophe De La Fuente Date: Mon, 17 Oct 2022 16:25:21 +0200 Subject: [PATCH 2/3] Fix `encode_utf16le` to avoid breaking with non-ASCI characters - Also add specs --- lib/net/ntlm/encode_util.rb | 2 +- spec/lib/net/ntlm/message/type3_spec.rb | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/net/ntlm/encode_util.rb b/lib/net/ntlm/encode_util.rb index 5a13e78..b800226 100644 --- a/lib/net/ntlm/encode_util.rb +++ b/lib/net/ntlm/encode_util.rb @@ -39,7 +39,7 @@ def self.decode_utf16le(str) # the function will convert the string bytes to UTF-16LE and note the encoding as UTF-8 so that byte # concatination works seamlessly. def self.encode_utf16le(str) - str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('UTF-8') + str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('ASCII-8BIT') end end end diff --git a/spec/lib/net/ntlm/message/type3_spec.rb b/spec/lib/net/ntlm/message/type3_spec.rb index 5249331..42792a1 100644 --- a/spec/lib/net/ntlm/message/type3_spec.rb +++ b/spec/lib/net/ntlm/message/type3_spec.rb @@ -222,4 +222,27 @@ end + describe '#serialize' do + context 'when the username contains non-ASCI characters' do + let(:t3) { + t2 = Net::NTLM::Message::Type2.new + t2.response( + { + :user => 'Hélène', + :password => '123456', + :domain => '' + }, + { + :ntlmv2 => true, + :workstation => 'testlab.local' + } + ) + } + + it 'serializes without error' do + expect { t3.serialize }.not_to raise_error + end + end + end + end From 088ea6386975f4933217bf166b753c3c1b659b88 Mon Sep 17 00:00:00 2001 From: Christophe De La Fuente Date: Wed, 12 Jun 2024 16:23:43 +0200 Subject: [PATCH 3/3] Pass the `unicode` flag and update the hash in specs --- spec/lib/net/ntlm_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/lib/net/ntlm_spec.rb b/spec/lib/net/ntlm_spec.rb index 247c70f..7114d3f 100644 --- a/spec/lib/net/ntlm_spec.rb +++ b/spec/lib/net/ntlm_spec.rb @@ -63,7 +63,7 @@ let(:user) { 'юзер' } it 'should return the correct ntlmv2 hash' do - expect(Net::NTLM::ntlmv2_hash(user, passwd, domain)).to eq(["ba3d357a20233dfc432b727537272bab"].pack("H*")) + expect(Net::NTLM::ntlmv2_hash(user, passwd, domain, { unicode: true })).to eq(["a0f4b914a37faeaee884b6b04a20faf0"].pack("H*")) end end