I was just reading an article called “A look at Heartbleed and why it really isnβt that bad” and, while I usually tend to agree with anyone who tries to fight against FUD, in this case it happens to be dangerously wrong. I’d write this as a blog comment rather than an entry on my own, but Tumblr seems firmly stuck in the 90’s and won’t even give me that option so here it goes…
In a nutshell, the article downplays the severity of the Heartbleed attack based on the Address Space Layout Randomization (ASLR) feature of most modern operating systems, that causes memory allocations to be randomized as a mitigation for buffer overflows. The reasoning goes: since memory allocations are random, and the Heartbleed bug allows you to read memory at random as well, the odds of reading important data are pretty much close to zero – therefore the Heartbleed attack is useless and you shouldn’t change your passwords.
Ouch.
This is based, I presume, on the following (wrong) assumptions:
If those assumptions were correct then the conclusion would be sound, but unfortunately, they are not. Memory allocation is a little more complicated than that. I’ll try to make a brief description of how memory layout works. There are much smarter people than me out there who have described it better, so if you’re interested in the topic, Google and Duck Duck Go are your friends. 
There are many other factual errors in the article, like confusing files with memory, mentioning Perfect Forward Secrecy when it’s not relevant at all, assuming the attack captures passwords from a database instead of from other users requests on transit, and so on. In fact, there are so many errors I suspect the author may just be pulling a prank on gullible readers! Still, let’s try to make things clearer. 
We must begin by making the distinction between memory space and memory addresses. Memory space is contiguous and shared for the entire machine, since there is only one RAM. However, the RAM is not accessed directly by user programs, but through the use of memory addresses that are assigned by the operating system. This mechanism is what prevents one program from directly accessing the memory of another program – each one is assigned a set of memory addresses, which maps to wherever the OS wants in RAM. It’s not user permissions or configuration that prevents it then, or whether the program is running as root or not, but simply that the same memory address for two different programs are mapped to different places in RAM. (There is an exception to this in which the OS can create shared memory between one or more programs, by mapping addresses to the same place in RAM, but it’s a special mechanism in which both programs cooperate to share a piece of memory and it’s not relevant to the Heartbleed bug). So whether the services that have the bug are running as root or not is not important at all, what’s important is what kind of information do these services have in their own memory.
The next thing we need to know is how memory allocation works. From the perspective of a high level language, like Python or Ruby, memory is a kind of magic: you just start using a new variable and it’s simply there, no questions asked.
That’s because such languages have mechanisms in place to shield the programmer from knowing how memory allocation actually works. More often than not, programmers who have never played with low level languages have some interesting misconceptions about it…
In reality, memory is allocated in pages, where each page is 4096 bytes long (could be a different size for non-Intel architectures, or in some other circumstances, but let’s pretend it’s always that size). In order to create new variables, you need some memory to hold their values, so the program needs to request this memory to the operating system. This is where the ASLR feature takes place: in older operating systems like, say, Windows NT, the new pages were assigned memory addresses in a sequential way, so it was easy to predict where data was. But in newer operating systems each page is given a random memory address.
…well, not exactly. If you request the operating system for a larger area than just 4096 bytes, you need those bytes to have contiguous memory addresses, or you wouldn’t know how to access them. So the only thing that’s random is the address of the first byte of contiguous memory you request. Also, it can’t be 100% random: since all pages are 4096 bytes in size, and you can only request a whole number of pages, it stands to reason that these random memory addresses will always be aligned to 4096 bytes. In fact, for efficiency reasons and depending on the operating system, they may even be aligned to larger numbers like 64 kilobytes. Still random, but not SO random. And that’s not the end of it.
See, 4096 bytes is an awful lot of space. You can’t just request a new page from the OS every time you need room for a single variable, which may only be a handful of bytes long. So what programs do is place variables inside two structures called stack and heap. What they do is request memory in pages, use the room to store many variables in them, and track down which variable is stored where in order to know which bytes in each page are available for new variables to be put in. As more and more variables are created, the stack and heap begin requesting new pages – and as variables are destroyed, the memory space they live in is marked as free to be reused by newly created variables.
The stack is used when creating variables that live in the scope of a function call, that’s why it’s called a stack: when you call another function, the local variables for it are stacked on top of the previous function. And when the function returns, its local variables are removed from the top of the stack, leaving their memory space free for the next call. All stack space is, by force, contiguous, at least for a single thread (each thread gets its own separate stack). Therefore, while the base memory address of the stack is random thanks to ASLR, the relative locations of variables within it are not – they are deterministic! They will depend on the order in which functions are called and how many local variables each one has. If you have a bug that lets you access memory past a stack variable, you’ll be accessing other variables in the stack without problems, despite of ASLR.
The heap, on the other hand, is for variables that can live outside the scope of a single function call. When you create a new variable with the new() operator in C++, or when you call the malloc() function in C, what the program is really doing is grabbing some available bytes from one of the pages of the heap and marking it as used by that variable. When you destroy that variable by calling free() in C or delete() in C++, you’re marking that space as free so other variables can use it. It follows then, that ASLR doesn’t help you all that much here either: even though the pages of the heap are more or less random (since they don’t all have to be contiguous as it happens with the stack) the variables stored in it are still grouped together. This mechanism is also deterministic, but since a server may have multiple users doing things at any given time, the pattern of memory use will depend on lots of factors you don’t know (user activity, basically), so the exact memory layout is hard to predict. Still, variables are being grouped together, and they won’t likely be nearby anything else like executable code or static data, which is not allocated in the heap nor the stack. So if you can read memory past the end of a variable, you’re likely to read data from other nearby variables, quite possibly related to what the vulnerable code was doing (in this case, decrypting SSL traffic from users).

In conclusion: while it’s always healthy not to panic
and the Internet has definitely seen worse bugs time and again, let’s not get carried away: if you used any service that contained the bug, you should be changing your passwords just to be on the safe side.
This security update resolves one publicly disclosed vulnerability and one privately reported vulnerability in Microsoft Exchange and Windows SMTP Service. The more severe of these vulnerabilities could allow denial of service if an attacker sent a specially crafted DNS response to a computer running the SMTP service.
However, researcher NicolΓ‘s Economou found an interesting surprise in this patch: two additional, undisclosed vulnerabilities had also been patched… and they were far more severe than the ones reported! From the Core Security advisory:
Nicolas found that the Windows SMTP Service does its own DNS resolution of MX records rather that use the DNS resolver from the operating system while investigating CVE-2010-0024.
Furthermore, he found that the patch referenced in MS10-024 fixed two severe bugs that were not disclosed as such in the bulletin and had no CVE identifiers assigned to them. Basic analysis of the vulnerabilities disclosed in this advisory indicates that the threat of DNS spoofing attacks against Windows SMTP service and Microsoft Exchange or of exploitation of CVE-2010-0024 was underestimated in MS10-024.
An attacker may leverage the two previously undisclosed vulnerabilities fixed by MS10-014 to spoof responses to any DNS query sent by the Windows SMTP service trivially. DNS response spoofing and cache poisoning attacks are well known to have a variety of security implications with impact beyond just Denial of Service and Information Disclosure as originally stated in MS10-024.
In fact, the two “new” vulnerabilities were quite crass. Both Exchange and the SMTP service were doing their own manually crafted DNS queries using incremental transaction IDs, which is a big no-no when implementing DNS because it makes it real easy for attackers to guess the transaction ID and spoof replies, as is a well known fact for… say… the last 16 years or so? But as it turns out, attackers didn’t even need to guess the transaction IDs… because they weren’t even being used when parsing the DNS responses! 
This omission may be easily attributed to the “embarrasment factor”
but it’s still a terrible idea to patch vulnerabilities silently: IT administrators, unaware of the real danger of the problem, may give the patch a lower priority. A denial of service just means having the mail server down for a while until it restarts, so the patch can wait – it’d be worse if the server didn’t work at all because patching went wrong. On the other hand, a DNS poison vulnerability means having an attacker browse through everyone’s emails and taking over all other services you may have on the same machine – patching becomes much more worth the risk.
Of course, this isn’t the first time this happens. Practically every vendor did this at one time or another. A quick Google search for “silently patched vulnerability” shows some 1.400.000 hits at the time I’m writing this, showing this is neither new or uncommon – and that even small software vendors may easily get caught. Especially thanks to the rise of binary diffing tools that can pinpoint precisely where and how the code was patched.
Thanks Alfredo Ortega for pointing out this advisory and providing such a cool sounding title. 
Given that Windows NT 4 was relased in ~1996 this vulnerability has been
present for ~14 years. If it is confirmed this vulnerablity is also
present in older systems such as Windows NT 3.1, released in ~1993,
Windows NTLMv1 authentication mechanism could have been vulnerable for
~17+ years.
Whoa. That’s kind of scary.
Kudos to Hernan Ochoa and Agustin Azubel for this great find! 
Below is the complete text of the advisory, except for the source code to the scripts, which were removed for brevity. The original advisory can be downloaded from: http://www.hexale.org/advisories/OCHOA-2010-0209.txt.
Update: Hernan Ochoa has also written an article regarding the risk assesment for this vulnerability.
Windows SMB NTLM Authentication Weak Nonce Vulnerability
Security Advisory
Hernan Ochoa ([email protected]) - Agustin Azubel ([email protected])
Title: Windows SMB NTLM Authentication Weak Nonce Vulnerablity
Advisory ID: OCHOA-2010-0209
Advisory URL: http://www.hexale.org/advisories/OCHOA-2010-0209.txt
Date published: 2010-02-09
Vendors contacted: Microsoft
Release mode: Coordinated release
Last Updated: 2010-02-09
Index
-----
1.Vulnerablity information
2.Vulnerablity description
3.Vulnerable systems
4.Vendor Information, solutions and workarounds
5.Credits
6.Technical description
6.1.NTLMv1 authentication protocol
6.2.The Flaws
6.3.Detecting if the SMB service generates duplicate 8-byte challenges
6.4.Exploiting duplicate challenges
6.4.1.Proof-of-Concept Exploit
6.5.Predicting challenges
6.5.1.SMB service: challenge generation process
6.5.2.Proof-of-Concept Exploit
7.References
8.Disclaimer
1.Vulnerability information
---------------------------
Impact: An unauthenticated remote attacker without any kind of
credentials can access the SMB service under the credentials of an
authorized user. Depending on the privileges of the authorized user, and
the configuration of the remote system, an attacker can gain read/write
access to the remote file system and execute arbitrary code by using
DCE/RPC over SMB.
Remotely Exploitable: Yes
Bugtraq Id:
CVE: CVE-2010-0231
2.Vulnerability description
---------------------------
Microsoft Server Message Block (SMB) Protocol is a Microsoft network
file sharing protocol also used for sharing printers, communications
abstractions such as named pipes and mailslots, and performing Remote
Procedure Calls (DCE/RPC over SMB) [1].
NTLM (NT Lan Manager) is a challenge-response authentication protocol
used by the SMB protocol [2].
Windows systems commonly use the SMB protocol with NTLM authentication
for network file/printer sharing and remote administration via DCE/RPC.
Flaws in Microsoft's implementation of the NTLM challenge-response
authentication protocol causing the server to generate duplicate
challenges/nonces and an information leak allow an unauthenticated
remote attacker without any kind of credentials to access the SMB
service of the target system under the credentials of an authorized
user. Depending on the privileges of the user, the attacker will be able
to obtain and modify files on the target system and execute arbitrary code.
3.Vulnerable Systems
--------------------
This vulnerability was verified by the authors on the following platforms:
Windows NT4 SP1
Windows Server 2003 SP2
Windows XP SP3
Windows Vista x32
Windows 7 x32 RC
However, all versions of Windows implementing NTLMv1 are suspected to be
affected.
Microsoft, in their "Microsoft Security Bulletin Advance Notification
for February 2010" [3], list the following platforms as affected:
Windows 2000 SP4
Windows XP SP2 and SP3
Windows XP Professional x64 Edition SP2
Windows Server 2003 SP2
Windows Server 2003 x64 Edition SP2
Windows Server 2003 SP2 for Itanium-based systems
Windows Vista
Windows Vista SP1
Windows Vista SP2
Windows Vista x64 Edition
Windows Vista x64 Edition SP1
Windows Vista x64 Edition SP2
Windows Server 2008 x32
Windows Server 2008 x32 SP2
Windows Server 2008 x64 SP2
Windows Server 2008 x64 SP2
Windows Server 2008 for Itanium-based systems
Windows Server 2008 for Itanium-based systems SP2
Windows 7 x32
See [3] for more details.
Given that Windows NT 4 was relased in ~1996 this vulnerability has been
present for ~14 years. If it is confirmed this vulnerablity is also
present in older systems such as Windows NT 3.1, released in ~1993,
Windows NTLMv1 authentication mechanism could have been vulnerable for
~17+ years.
4.Vendor Information, Solutions and Workarounds
-----------------------------------------------
SMB NTLM Authentication Lack of Entropy Vulnerability - CVE-2010-0231
http://www.microsoft.com/technet/security/bulletin/ms10-012.mspx
5.Credits
---------
This vulnerability was discovered by Hernan Ochoa (Independent
Information Security Consultant and Researcher) and it was researched by
Hernan Ochoa and Agustin Azubel (Independent Information Security
Consultant and Researcher).
6.Technical description
------------------------
Microsoft Server Message Block (SMB) Protocol is a Microsoft network
file sharing protocol also used for sharing printers, communications
abstractions such as named pipes and mailslots, and performing Remote
Procedure Calls (DCE/RPC over SMB) [1].
NTLM (NT Lan Manager) is a challenge-response authentication protocol
used by the SMB protocol [2].
Windows systems commonly use the SMB protocol with NTLM authentication
for network file/printer sharing and remote administration via DCE/RPC.
Flaws in Microsoft's implementation of the NTLM challenge-response
authentication protocol causing the server to generate duplicate
challenges/nonces and an information leak allow an unauthenticated
remote attacker without any kind of credentials to access the SMB
service of the target system under the credentials of an authorized
user. Depending on the privileges of the user, the attacker will be able
to obtain and modify files on the target system and execute arbitrary code.
6.1.NTLMv1 authentication protocol
----------------------------------
The NTLMv1 authentication protocol is a challenge-response protocol that
consists of the following messages:
1. The client sends to the server a message containing a set of flags of
features supported/requested to perform authentication.
2. The server responds with a message containing a set of flags
supported/required by the server enabling both ends to agree on the
authentication parameters and, more importantly, an 8-byte random
challenge/nonce.
3. The client uses the random challenge/nonce and the user's
credentials to calculate the response (24 bytes) and sends it to the server.
4. The server determines if the response is correct and allows or
disallows access to the client.
The randomness of the 8-byte challenge/nonce returned by the server
tries to ensure that every challenge-response sequence is unique helping
protect against replay attacks.
6.2.The Flaws
----------------
Several flaws were found leading to attacks such as generation of
duplicate challenges/nonces and challenge/nonce prediction.
The randomness of the 8-byte challenges generated by the SMB server in
response to an specific packet requesting authentication is bad enabling
attackers to perform replay attacks. The SMB server easily generates
duplicate 8-byte challenges.
The challenge/nonce prediction attack is feasible due to several factors
including that the protocol leaks information that can be used by an
attacker to calculate the internal state of the PRNG used to generate
challenges.
6.3.Detecting if the SMB service generates duplicate 8-byte challenges
-----------------------------------------------------------------------
Detecting the generation of duplicate challenges can be verified
remotely by repeatedly sending 'SMB Negotiate Protocol Request' packets
to a Windows system with the 'Flags2' field set to 0xc001 (disabling
security signatures, extended attributes and extended security
negotiation) recording the 8-byte challenges obtained from the server
and waiting for duplicates.
The following Ruby script can be used to test for the presence of this
vulnerability:
6.4.Exploiting duplicate challenges
--------------------------------------
There are different ways to exploit duplicate challenges, including:
(i) An attacker A can eavesdrop network traffic looking for NTLM
authentication messages exchanged between client C and server S ('SMB
Negotiate Protocol Requests' packets and 'SMB Negotiate Protocol
Responses' packets), storing challenges and their corresponding
responses. The attacker A can then perform several authentication
requests to server S until S returns a previously observed challenge (a
duplicate).At that point attacker A will send the corresponding and
previously recorded response.
We did not find so far any current Windows version (XP,Vista,7,etc)
that by default or using some specific configuration, when acting as an
SMB client, would generate the necessary 'SMB Negotiate Protocol
Request' packets with the correct values in the 'Flags2' field to
trigger the vulnerability when accessing a remote SMB service. Hence we
were unable to collect duplicate challenges only by network sniffing.
Tests were performed with the third-party SMB client 'smbclient' from
the SAMBA project with the same negative results (tests were not
exhaustive).
Since this problem was also found on Windows versions as old as Windows
NT4, this scenario might still be possible.
(ii) An attacker A connects to system S and sends mutiple 'SMB
Negotiate Protocol Request' packets with the 'Flags2' field set to
0xc001 to obtain several challenges, and stores them. The attacker A
then forces a user U on system S to connect to his own specially crafted
SMB server, for example by sending an email with multiple
tags
with UNC links (e.g.:
) or a link to
web server with similar
tags. Upon receiving the connections from
system S,the attacker's SMB server will respond with the previously
obtained challenges and will store the corresponding responses returned
by the remote system. Attacker A has now a set of responses which are
the challenges encrypted with user's U credentials.
Finally, the attacker A will perform several authentication requests to
system S until it returns one of the challenges obtained at the
beginning of this attack, and at that point he will replay the
corresponding and previously obtained response to gain access to system
S as user U.
If user U has, for example, local administrator privileges on system S
(not uncommon for Windows XP users, for example), remote code execution
is possible via DCE/RPC over SMB. Even if user U has no administrator
privileges attacker A can still access, for example, file shares
accessible by user U and read/modify information.
Tests performed showed that challenges and responses obtained from a
system S can be reused multiple times against that same system and other
remote systems. We observed that challenges obtained from a system S
were also returned by other remote systems. This means that attacker A
only needs, in the best case scenario, to force user U to connect to his
own specially crafted SMB server once. Of course, user U must have
access (his credentials must be valid) to the other systems attacked.
This attack needs the victim to have port 445/tcp open and the attacker
to be able to access that port. The victim also needs to be able to
access port 445/tcp on the attacker's server (only once, to record
responses. Subsequent attacks do not need the victim to access the
attacker's system).
This simple attack using a 'brute-force' approach to find duplicate
challenges proved to be acceptably effective.
6.4.1.Proof-of-Concept Exploit
--------------------------------
The exploit implementation is twofolded:
(i) setup_smb_weak_nonce.rb
This standalone Ruby script performs several connections to the victim
sending 'SMB Negotiate Protocol Request' packets to obtain 8000
challenges (the number of challenges to be obtained can be changed).
After collecting 8000 challenges, it will listen on port 445/tcp for
incoming SMB connections originated by the victim. For every connection
received, it will send to the victim one of the previously obtained
challenges and will store the corresponding response obtained.
As a simple example of a method to force the victim to connect to the
attacker, the file 'conn.html' is provided. This is a very simple HTML
file with javascript code that will generate 1000
tags with an UNC
link to different image files.
The challenges and responses obtained are saved to the file
'fullcreds.log'.
(ii) msf_smb_weak_nonce.rb
This metasploit module will perform connections to the victim until
the server responds with one of the duplicate challenges stored in
'fullcreds.log'. The module will then send the corresponding response to
gain access to the victim's SMB service.
Finally, after successful exploitation, the module will create the
file 'owned.txt' in the ADMIN$ share (c:\windows) with the following
text: "Windows SMB NTLM Authentication weak nonce vulnerability
successfully exploited!".
This module can be easily modified to execute code on the remote
system (given the target user has enough privileges).
To exploit the vulnerability repeat the following steps:
1. copy msf_smb_weak_nonce.rb to
/modules/exploits/windows/smb
2. Run setup_smb_weak_nonce.rb specifying the IP of the victim (e.g.:
ruby setup_smb_weak_nonce.rb 192.168.10.1). After collecting the nonces
the script will listen on port 445 for incoming SMB connections.
3. Run Internet Explorer and load 'conn.html'. This will produce 1000+
connections to the SMB server implemented by setup_smb_weak_noce.rb.
(Note 1: setup_smb_weak_nonce.rb needs to be run as root to be able to
listen on port 445/tcp)
(Note 2: If you load 'conn.html' with Internet Explorer and
'conn.html' is stored on a local drive (e.g.:c:\conn.html) it is
possible Internet Explorer will prompt you to allow execution of the
javascript code within 'conn.html'. This is not a limitation of the
attack, it is just an extra protection implemented by Internet Explorer,
the 'conn.html' does not even need to contain javascript code, it uses
it just because it is convenient, you could just as easily 'hard-code'
all
tags. Also, loading the html file from the a local disk is not
a real attack scenario, all of this is for demonstration purposes).
4.After 1000 connections are received by setup_smb_weak_nonce.rb the
script will terminate. The file 'fullcreds.log' will be generated. Copy
'fullcreds.log' to /tmp.
5. run metasploit (msfconsole) and execute the following commands:
-use windows/smb/msf_smb_weak_nonce
-set RHOST
for example: set RHOST 192.168.10.1
-set payload windows/shell/bind_tcp
-exploit
The metasploit module looks for 'fullcreds.log' in '/tmp' by default.
You can specify the location of the 'fullcreds.log' file using the
following command:
-set CREDSFILE
for example:
-set CREDSFILE /mydir/fullcreds.log
6.the metasploit module will start performing connections to the
victim until receiving a duplicate challenge for which there's a
response in the 'fullcreds.log' file. After successfully authenticating
to the victim, the script will create the file 'owned.txt' in c:\windows
via the ADMIN$ share (given the user exploited has enough privileges).
Please remember that this proof-of-concept exploit requires the targer
user to have enough privileges (e.g.: local administrator) to access the
ADMIN$ share remotely. However, the target user does need to have this
privilege level in order for the attacker to exploit the vulnerability.
For example: if the target user only has regular user privileges, an
attacker can access the file shares that user has access to. Also,
exploiting the vulnerabiliy and the level of access obtained are two
different things.
This is just a proof-of-concept exploit, it can be improved and optimized.
Next are all the previously mentioned files part of the proof-of-concept
exploit:
6.5.Predicting challenges
The challenge/nonce prediction attack is feasible due to several
factors including that the protocol leaks information that can be used
by an attacker to calculate the internal state of the PRNG used to
generate challenges.
In order to explain the attack implemented next we begin by explaining
the method used by the Windows SMB service to generate challenges.
6.5.1.SMB service: challenge generation process
(Note: during this explanation we are going to use the code for the
Windows XP version of all modules mentioned. The code is the same in all
platforms with some minor differences for some platforms but these
differences do not produce a different behaviour).
The function that generates the challenges returned in 'SMB Negotiate
Protocol Response' packets is srv.sys!GetEncryptionKey():
It takes the current time, and adds to the low part of the current time
the value of the
global variable _EncryptionKeyCount.
00040735 lea eax, [ebp+CurrentTime]
00040738 push eax
00040739 call ds:__imp__KeQuerySystemTime@4
0004073F mov eax, _EncryptionKeyCount
00040744 add dword ptr [ebp+CurrentTime], eax
Increments _EncryptionKeyCount by 0x100 and makes some 'calculations'
with the (current time.lowpart + _EncryptionKeyCount) resulting in a
DWORD value with the
following 'pattern':
where CT = (current time.lowpart + _EncryptionKeyCount)
seed = CT[1], CT[2]-1, CT[2], CT[1]+1;
00040747 movzx ecx, byte ptr [ebp+CurrentTime+1]
0004074B movzx eax, byte ptr [ebp+CurrentTime+2]
0004074F add _EncryptionKeyCount, 100h
00040759 mov edx, ecx
0004075B shl edx, 8
0004075E lea esi, [eax-1]
00040761 or edx, esi
00040763 mov esi, ds:__imp__RtlRandom@4
00040769 shl edx, 8
0004076C or edx, eax
0004076E shl edx, 8
00040771 inc ecx
00040772 lea eax, [ebp+Seed]
00040775 or edx, ecx
Then it calls the ntoskrnl.exe!RtlRandom(&seed) function three times, using
as a 'seed' the value with the pattern shown above. Each call to
ntosrnkl.exe!RtlRandom(&seed)
returns in 'seed' a different value (meaning each call does not use the
same value as a 'seed').
00040777 push eax
00040778 mov [ebp+Seed], edx
0004077B call esi ; RtlRandom(x)
0004077D mov [ebp+var_18], eax
00040780 lea eax, [ebp+Seed]
00040783 push eax
00040784 call esi ; RtlRandom(x)
00040786 mov ebx, eax
00040788 lea eax, [ebp+Seed]
0004078B push eax ; Seed
0004078C call esi ; RtlRandom(x)
The calls to ntoskrnl.exe!RtlRandom(&seed) generate 3 'random' numbers.
Based on the value of random_number3, random_number1 and random_number2 are
modified:
0004078E test al, 1
00040790 mov ecx, 80000000h
00040795 jz short loc_4079A
00040797 or [ebp+var_18], ecx
0004079A
0004079A loc_4079A:
0004079A test al, 2
0004079C jz short loc_407A0
0004079E or ebx, ecx
000407A0
000407A0 loc_407A0:
Finally, the code returns the challenge in the form
bytes(random_number1, random_number2)
000407A0 mov eax, [ebp+var_18]
000407A3 mov ecx, [ebp+var_4]
000407A6 mov [edi+4], ebx
000407A9 mov [edi], eax
000407AB pop edi
000407AC pop esi
000407AD pop ebx
000407AE call @__security_check_cookie@4
000407B3 leave
000407B4 retn 4
Next is pseudo-code for the function srv.sys!GetEncryptionKey():
// Global Variable
DWORD _EncryptionKeyCount = 0;
srv.sys!GetEncryptionKey(byte OUT *pChallenge)
{
LARGE_INTEGER currentTime;
DWORD seed;
DWORD random_number1, random_number2, random_number3;
KeQuerySystemTime(&CurrentTime);
CurrentTime.LowPart += _EncryptionKeyCount;
_EncryptionKeyCount += 0x100;
CT = CurrentTime.LowPart;
seed = CT[1], CT[2]-1, CT[2], CT[1]+1;
random_number1 = ntoskrnl.exe!RtlRandom(&seed);
random_number2 = ntoskrnl.exe!RtlRnadom(&seed);
random_number3 = ntoskrnl.exe!RtlRandom(&seed);
if ( (random_number3 & 1) == 1) {
random_number1 |= 0x80000000
}
if( (random_number3 & 2) == 2 ) {
random_number2 |= 0x80000000
}
*pChallenge = bytes(random_number1, random_number2);
}
The code for ntoskrnl.exe!RtlRandom(&seed) is the following:
It receives the seed and performs the following calculations:
X0 = *seed;
X1 = (a*X0 + b ) mod m
where:
a = 0x7FFFFFED
b = 0x7FFFFFC3
m = 0x7FFFFFFF
004B5B75 mov edi, edi
004B5B77 push ebp
004B5B78 mov ebp, esp
004B5B7A push ebx
004B5B7B push esi
004B5B7C mov esi, [ebp+Seed]
004B5B7F mov eax, [esi]
004B5B81 imul eax, 7FFFFFEDh
004B5B87 push edi
004B5B88 mov ecx, 7FFFFFC3h
004B5B8D add eax, ecx
004B5B8F mov edi, 7FFFFFFFh
004B5B94 xor edx, edx
004B5B96 mov ebx, edi
004B5B98 div ebx
With the X1 value performs similar calculations:
X2 = (a*X1 + b) mod m
004B5B9A mov ebx, edx
004B5B9C mov eax, edx
004B5B9E imul eax, 7FFFFFEDh
004B5BA4 add eax, ecx
004B5BA6 xor edx, edx
004B5BA8 div edi
It sets the value of seed to X2
004B5BAA pop edi
004B5BAB mov [esi], edx
it calculates (X2 & 0x7F) to generate an index for the
_RtlpRandomConstantVector
004B5BAD and edx, 7Fh
004B5BB0 lea ecx, _RtlpRandomConstantVector[edx*4]
and finally fetches the value found at the previously calculated index,
and also
stores the value of X1 in that position.
004B5BB7 mov eax, [ecx]
004B5BB9 pop esi
004B5BBA mov [ecx], ebx
Next is pseudo-code for the function ntoskrnl.exe!RtlRandom:
// Global variable
DWORD ntoskrnl.exe!RtlpRandomConstantVector [128] = {...}
DWORD ntoskrnl.exe!RtlRandom(DWORD *pseed)
{
DWORD a = 0x7FFFFFED;
DWORD b = 0x7FFFFFC3;
DWORD m = 0x7FFFFFFF;
DWORD X0, X1, X2;
X0 = *pseed;
X1 = ( a*X0 + b ) mod m
X2 = ( a*X1 + b ) mod m
*pseed = X2;
ndx = X2 & 0x7F;
n = RtlpRandomConstantVector[ndx];
RtlpRandomConstantVector[ndx] = X1;
return n;
}
In Summary,
The srv.sys!GetEncryptionKey() does the following:
- Gets current time, takes the low part (4 bytes) and adds the value
of _EncryptionKeyCount (4-bytes)
- Increments _EncryptionKeyCount by 0x100
- Takes the two 'middle' bytes of CT=(current time.lowpart +
_EncryptionKeyCount) and creates
a seed with the form CT[1], CT[2]-1, CT[2], CT[1]+1.
- Calls ntoskrnl.exe!RtlRandom three times and obtains three random
numbers (random1,random2,random3)
- Depending on the value of random3, makes some modifications to
random1 and random2
- creates the challenge by creating an array of bytes in the form
random1, random2
The ntoskrnl.exe!RtlRandom function appears to be a Maclaren-Marsaglia
PRNG algorithm using two LCGs (linear congruential generators) [4] with
a vector of 128 bytes.
We know the following facts:
- _EncryptionKeyCount starts with a value of 0
- _EncryptionKeyCount is only modified by srv.sys!GetEncryptionKey.
The code that calls srv.sys!GetEncryptionKey() is not regularly
triggered, but only when the SMB service receives a packet like the one
we use with the 'flags2' field set to 0xc001
- We have not observed 'modern' Windows systems (Windows XP SP3,
Vista, 7, etc) generate these kind of packets
- This allows us to expect that before start conducting an attack
against any 'modern' Windows system, _EncryptionKeyCount will always be
0; by keeping count of the number of packets we send, we can also
calculate the value of _EncryptionKeyCount for further connections
- Interestingly enough, in our tests, the value of Current Time used
by srv.sys!GetEncryptionKey to generate the seed was the same value
returned by the SMB service to the client in the field 'System Time' of
an 'SMB Negotiate Protocol Response' packet
- The initial state of the vector used by ntoskrnl.exe!RtlRandom is
hard-coded, but it is modified every time the function is called and it
is called every time a new process is created (modifications might not
be that many).
Based on these facts we implemented the following attack to predict
challenges:
- We set the vector used by ntoskrnl.exe!RtlRandom to a 'known state'
-To do this we send multiple 'SMB Negotiate Protocol Request' packets
with the 'flags2' field set to 0xc001 to trigger
srv.sys!GetEncryptionKey which in turns calls ntoskrnl.exe!RtlRandom
modifying its internal vector (~300 packets)
-Since we know the seed used by the server to perform the previous
actions, because it is in the 'System Time' field of the 'SMB Negotiate
Protocol Response' packet we receive, and we also know all the other
variables including the value of _EncryptionKeyCount, we can do the same
calculations updating our own vector
-We repeat this process until all 128 values of our vector are
calculated. At this point we know the state of the table on the remote
system, we know all of its values and their position within the vector.
- We calculate all possible challenges that can be generated with that
'known state' next time srv.sys!GetEncryptionKey is called
- We force the victim to connect to our specially crafted SMB server
to get all those challenges encrypted with the credentials of the victim
(an average of ~16000 to ~48000 possible challenges)
- At this point we know that if we send another authentication request
to the victim the challenge returned will be one of the pre-calculated
challenges. We make the connection, get the challenge, look for the
corresponding response we obtained from the victim, and authenticate to
the SMB service.
6.5.2.Proof-of-Concept Exploit
------------------------------
Next are the necessary steps to perform the attack:
- Run predictor.rb against the victim. E.g.: ruby predictor.rb
192.168.1.110
This script will show the progress of 'setting' the values of the
victims RtlRandom's internal vector.
It will display something like this:
(0x00-0x04) 0x00000000 0x00000000 0x00000000 0x2948d15b
(0x04-0x08) 0x72f4dda5 0x00000000 0x14dbf86f 0x00000000
(0x08-0x0c) 0x00000000 0x62d2c31e 0x00000000 0x7ef9db03
(0x0c-0x10) 0x00000000 0x0dfdee4d 0x00000000 0x0ecd0d97
(0x10-0x14) 0x00000000 0x04d986e1 0x00000000 0x00000000
(0x14-0x18) 0x00000000 0x35fdf275 0x00000000 0x00000000
(0x18-0x1c) 0x00000000 0x47b6b289 0x00000000 0x00000000
(0x1c-0x20) 0x5b9a7eb8 0x00000000 0x00000000 0x3b150ecc
(0x20-0x24) 0x146909b1 0x7a3022b1 0x00000000 0x00000000
(0x24-0x28) 0x23bfb6e0 0x00000000 0x00000000 0x0e5c7c0f
(0x28-0x2c) 0x3f027a59 0x00000000 0x00000000 0x00000000
(0x2c-0x30) 0x00000000 0x00000000 0x6a3158d2 0x00000000
(0x30-0x34) 0x69d97001 0x2cd5c5e6 0x00000000 0x2cdcb5b0
(0x34-0x38) 0x00000000 0x00000000 0x00000000 0x00000000
(0x38-0x3c) 0x00000000 0x00000000 0x00000000 0x00000000
(0x3c-0x40) 0x08deca3d 0x4954003d 0x00000000 0x00f5b207
(0x40-0x44) 0x4de0efd1 0x00000000 0x00000000 0x56bf3780
(0x44-0x48) 0x25210c65 0x00000000 0x00000000 0x00000000
(0x48-0x4c) 0x00000000 0x00000000 0x00000000 0x00000000
(0x4c-0x50) 0x00000000 0x00000000 0x00000000 0x00000000
(0x50-0x54) 0x00000000 0x397415a1 0x34aa91eb 0x00000000
(0x54-0x58) 0x231aeb35 0x00000000 0x00000000 0x00000000
(0x58-0x5c) 0x00000000 0x04223749 0x00000000 0x1b4c91f8
(0x5c-0x60) 0x00000000 0x00000000 0x00000000 0x71ad9da7
(0x60-0x64) 0x00000000 0x00000000 0x00000000 0x046696bb
(0x64-0x68) 0x00000000 0x00000000 0x193b264f 0x439ef5b4
(0x68-0x6c) 0x5bdd2f34 0x00000000 0x00000000 0x481eaee3
(0x6c-0x70) 0x00000000 0x00000000 0x50b1e1f7 0x2a8d71dc
(0x70-0x74) 0x00000000 0x02240f41 0x0ae7948b 0x37af3d8b
(0x74-0x78) 0x00000000 0x00000000 0x77130a3a 0x640bf49f
(0x78-0x7c) 0x31665169 0x20a1c769 0x00000000 0x00000000
(0x7c-0x80) 0x6958e618 0x00000000 0x00000000 0x00000000
known values: 48/128
- When predictor.rb finishes, it writes the values of the vector to
'x_values.log' (it also generates a file 't_values.log' containing the
'current times' observed in the 'SMB Negotiate Protocol Response' packets).
- Run generate_challenges.rb, it will generate the file
'challenges.log' with all the possible challenges based on 'x_values.log'.
- Run savecreds.rb, it will wait for incoming connections on port 445/tcp
- On the victim, use 'predict.html' with Internet Explorer to perform
SMB connections to savecreds.rb's server
You will need to change the IP address of the server where savecreds.rb
is running in 'predict.html', and
also the number of connections to perform (look for the line: 'if (id
== 50000) {' and change accordingly).
The number of connections that need to be performed is shown by
savecreds.rb.
- When savecreds.rb is finished, a file 'fullcreds.log' will be created
- Now use the metasploit module msf_smb_weak_nonce.rb as explained
before with the recently generated 'fullcreds.log' against the victim
- You should be able to authenticate with the victim at the ~first attempt
Sometimes the challenge is correctly 'guessed' at the first attempt,
but the attack fails because of some SMB error. If this happens please
note that the challenge was indeed correctly predicted.
Also note that since the internal vector is not completely modified
after just one connection, the exploit will actually be able to predict
more challenges (you might be able to run the metasploit exploit
multiple times before performing the whole attack all over again).
The predictor.rb assumes the EncryptionKeyCount is 0. If you want to
run the attack multiple times you
just need to modify its value in predictor.rb. The value of
EncryptionKeyCount after the attack is displayed by predictor.rb when it
terminates (you need to use the value displayed + 0x100).
After generate_challenges.rb is executed, if the number of possible
challenges is 'too big' (~48000 or more) you
might want to run predictor.rb again. The size of the set of possible
challenges vary according to the values in the vector. Remember to
adjust EncryptionKeyCount before running predictor.rb. We recommend
peforming the attack when EncryptionKeyCount is 0 specially if this is
the first time this proof-of-concept is used.
This is just a proof-of-concept exploit, it can be improved and optimized.
7.References
------------
[1] Microsoft SMB Protocol and CIFS Protocol Overview
http://msdn.microsoft.com/en-us/library/aa365233(VS.85).aspx
[2] Microsoft NTLM
http://msdn.microsoft.com/en-us/library/aa378749(VS.85).aspx
[3] Microsoft Security Bulletin Advance Notification for February 2010
http://www.microsoft.com/technet/security/Bulletin/ms10-feb.mspx
[4] Bruce Schneier, Applied Cryptography (Second Edition), 1996.
Chapter 16, pp 369.
8.Disclaimer
------------
The contents of this advisory are copyright (c) 2010 Hernan Ochoa, and
may be distributed freely provided that no fee is charged for
distribution and proper credit is given.
]]>