Wednesday, April 22, 2015

The Assembly Behind MS15-034

Recall I previously blogged about the MS15-034 DoS vulnerability that could blue screen a Windows Server with a simple HTTP request.

Mike Czumak of securitysift.com blogged about the Assembly code behind that Http.sys vulnerability and I thought it might be interesting to attempt to explain a bit of the Assembly language code screenshot in that article.

First here is the code from the article. If you scroll down I'll walk through it line by line.

Unpatched
move ecx, [esi+4]
move eax, [esi]
cmp edx, ecx
jb short loc_6EEF9

loc_6EEF9:
   sub eax, edi
   sbb ecx, edx
   add eax, 1
   adc ecx, 0
   mov [esi], eax
   move [esi+4], ecx


Patched
move ecx, [esi+4]
move eax, [esi]
cmp edx, ecx
jb short loc_6F112

loc_6F112:
   push esi
   push 0
   sub eax, edi
   push 1
   sbb ecx, edx
   push ecx
   push eax
   call _RtlULongLongAdd@20 ;
   test eax, eax
   jl short loc_6F184


First, in both the patched & unpatched version there are 4 lines of code that end with a jump.

move ecx, [esi+4]
move eax, [esi]
cmp edx, ecx
jb short LABEL


First recall that assembly used "registers" to store values for quick use. Registers like 'edi' and 'esi' are index registers used to walk yourself through the stack, arrays, as a pointer, etc. Second recall that registers like 'ecx' and 'edx' and 'eax' are general purpose registers used for doing your mathematics, comparisions, and storing temporary values, etc. Now in this case the first 2 lines are "moving values from the stack into your general purpose registers". Note that in this scenario 'eax' is the upper Range Header value in the MS15-034 vulnerablity (Recall: 18 – 18446744073709551615). In this case 'eax' would have 18446744073709551615 in it. Next is a 'cmp' operand which performs a comparison of the 2 general purpose registers. If edx < ecx then it sets the 'Carry Flag' to true. This is determining if there are any more values to check. The last line is the 'jb' (jump if below) operand which basically says that it will jump if that 'Carry Flag' is true of if edx < ecx. So it jumps to our label that contains the vulnerable code.

loc_6EEF9:
   sub eax, edi
   sbb ecx, edx
   add eax, 1
   adc ecx, 0
   mov [esi], eax
   move [esi+4], ecx


Now in the unpatched 6 lines of code above, it's basically subtracting your lower range header (edi or 18) from your upper range header value (eax or 18446744073709551615). It's also doing an 'sbb' or subtract with borrow on those counters so it'll know how many tests it has yet to perform. Then is does an 'add', setting eax (our upper range header value or 18446744073709551615) to eax + 1. Here in lies the problem! There is an overflow condition that can happen here. The next line of code is an 'adc' which add with carry and keeps those counters moving. Then the final 2 'mov' commands 'move the values of eax and ecx back onto the stack' (just as they were before the function started).

loc_6F112:
   push esi
   push 0
   sub eax, edi
   push 1
   sbb ecx, edx
   push ecx
   push eax
   call _RtlULongLongAdd@20 ;
   test eax, eax
   jl short loc_6F184


Finally let's look at the patched version. Instead of doing that eax + 1 without a check, it does the overflow check by calling the _RtlULongLongAdd function. Now you can't call a function like that without parameters. It takes 5 parameters, so you push those 5 parameters onto the stack (esi, 0, 1, ecx, and eax). Prior to that you also update those counters to keep things moving. Finally you do a 'test' combined with a 'jl' which basically checks to see if after your addition of 2 unsigned integers, did you end up with a negative number? (if so that means you overflow, if not you're good). Thus we are now patched because we're checking and handling the case where we overflow on that eax + 1.

Hope that helps a bit, and happy patching!

Copyright © 2015, this post cannot be reproduced or retransmitted in any form without reference to the original post.

No comments:

Post a Comment