Thursday, September 25, 2014

Using ASAN as a protection

AddressSanitizer, or ASAN, is an excellent tool for detecting subtle memory errors at runtime in C / C++ programs. It is now a productionized option in both cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 clang and gcc compilers, and has assisted in uncovering literally thousands of security bugs.

ASAN works by instrumenting compiled code with careful detections for runtime errors. It is primarily a detection tool. But what if we attempted to use is as a tool for protection?

The case for using ASAN-compiled software as a protection is an interesting one. Some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 most severe vulnerabilities are memory corruptions used to completely compromise a victim's machine. This is particularly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case for a web browser. If an ASAN-compiled build can help defend against cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se bugs, perhaps it has value to some users? An ASAN build is slower enough that no production software is likely to ship compiled with ASAN. But cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 slow down is not so bad that a particularly paranoid user wouldn't be able to easily accept it on a fast machine.

With that trade-off in mind, let's explore: does ASAN actually provide protection? To answer that, let's break memory corruption down into common vulnerability classes:

1. Linear buffer overflow (heap, stack, BSS, etc.)
A linear buffer overflow is one where every byte past cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 end of a buffer is written in sequence, up to some end point (example). For example, a memcpy() or strcpy() based overflow is linear. Because of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 way ASAN works, I believe it will always catch a linear buffer overflow. It uses a default "redzone" of at least 16 bytes, i.e. touching _any_ address within 16 bytes of a valid buffer will halt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 program with an error. Under ASAN, a linear buffer overflow condition will always hit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 redzone.
This is great news because linear buffer overflows are one of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 more common types of security bugs, and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are quite serious, affording cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker a lot of control in corrupting program state.

2. Non-linear buffer overflow
A non-linear buffer overflow is one where data is written at some specific (but often attacker-controlled) out-of-bounds offset relative to a buffer (example). These bugs can be extremely powerful. Unfortunately, because of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir power, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are both favored by attackers and also not stopped by ASAN if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker knows cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are targeting an ASAN build. Example C program:

int main()
{
  char* p = malloc(16);
  char* p2 = malloc(16);
  printf("p, p2: %p, %p\n", p, p2);
  p2[31] = '\0';
}

Compile it with ASAN (clang -fsanitize=address) and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n run it and no error will be detected. The bad dereference "jumps over" cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 redzone to corrupt p2 via pointer p.

3. Use-after-free / double-free
ASAN does detect use-after-frees very reliably in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 conditions that matter for current use cases: normal usage, and under fuzzing. However, if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 attacker is specifically targeting an exploit against an ASAN build, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can pull tricks to still attempt cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit. By churning cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 memory allocator hard (as is trivially possible with JavaScript), cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 condition can be hidden. Example C program:

int main()
{
   int n = 257 * 1024 * 1024;
   char* p2;
   char* p = malloc(1024);
   printf("p: %p\n", p);
   free(p);
   while (n) {
     p2 = malloc(1024);
     if (p2 == p) printf("reused!!\n");
     free(p2);
     n -= 1024;
   }
   n = 30 * 1024 * 1024;
   while (n) {
     p2 = malloc(1024);
     if (p2 == p) printf("reused!!\n");
     n -= 1024;
   }
   p[0] = 'A';
}

The bad reference is not trapped with default ASAN values. The default values can be changed such that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bad reference is trapped:

ASAN_OPTIONS=quarantine_size=4294967295 ./a.out

It's a shame that setting this value to "unlimited" may not be possible due to a probable integer truncation in parameter parsing, see how this behaves differently:

ASAN_OPTIONS=quarantine_size=4294967296 ./a.out

4. Uninitialized value
Uninitialized values are harder to categorize. The impact varies drastically depending on where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uninitialized value is a pointer or an integer. For example, for an uninitialized pointer, effects similar to "non-linear buffer overflow" might even apply. Or if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uninitialized value is a copy length cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n perhaps it's more similar to "linear buffer overflow".
Or, if it's an uninitialized raw function pointer, that's a bigger problem. Indirect jumps are not checked. The behavior of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following ASAN-compiled program is instructive (run it in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 debugger):

void subfunc1()
{
  unsigned long long blah = 0x0000414141414141ull;
}

void subfunc2()
{
  int (*funcptr)(void);
  funcptr();
}

int main()
{
  subfunc1();
  subfunc2();
}

If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 uninitialized value is a pointer to a C++ class cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n similar (indirect) problems apply.

5. Bad cast
The effects of a bad cast are fairly varied! Perhaps cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bad cast involves mistakenly using an integer value as a pointer. In this instance, effects similar to "non-linear buffer overflow" might be achievable. Or perhaps if a pointer for a C++ object is expected, but it is mistaken with a pointer to a raw buffer, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n a bad vtable gets used, leading to program flow subversion. One final C++ example to illustrate this. Run under ASAN to observe a raw crash trying to read a vtable entry from 0x0000414141414141:

class A
{
public:
  long long val;
};

class B
{
public:
  virtual void vfunc() {};
};

int main()
{
  class A a;
  a.val = 0x0000414141414141ull;
  class B* pb = (class B*) &a;
  pb->vfunc();
}


Safer ASAN?
There's certainly scope for a safer variant of ASAN, specifically designed to provide safety racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than detection. It would be based on various changes:

  • Change cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dereference check from "is this dereference address ok?" to "is this address in bounds for this specific pointer?". This takes care of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 nasty "non-linear buffer overflow" as well as some of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 worst effects of bad casts. This is not an easy change.
  • Initialize more variables: pointer values on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack and heap. (This is not as easy as it sounds, particularly for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap case, where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 casting operator may become a point of action.)
  • Make cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 quarantine size for use-after-free unlimited. This burns a lot of memory, of course, but may be acceptable if fully unused pages are returned to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 system with madvise() or even a crazy remap_file_pages() trick.


Remaining risks

Of course, even a "safer ASAN" build would not be bullet-proof. Taking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 specific case of an safer-ASAN compiled Chromium, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re would still be additional attacks possible:

  • Plug-ins. Many plug-ins are closed source and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365refore cannot be replaced with ASANified versions. The safer build of Chromium would have plug-ins disabled: --disable-plugins or even at compile time.
  • Native attack surfaces called by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 browser. For example, what happens when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 browser encounters a web font. It'll probably get passed to a system library which parses this dangerous format using native code. In extreme cases, such as older Chromium on Windows, fonts were parsed in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel(!). --disable-remote-fonts, probably ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r flags.
  • Native attack surfaces triggerable by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 browser. Less obviously, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re can be operating system mechanisms that kick in simply because a file is downloaded or appears on disk. Anti-virus is notoriously buggy in this regard.
  • The v8 JIT engine. Any logic error in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 JIT engine resulting in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 emission of bad opcode sequences, or sequences with buggy bounds checks, are pretty toxic.
  • Pure logic vulnerabilities. UXSS vulnerabilities will remain unmitigated. In extremely rare but spectacular cases, unsandboxed code execute has been achieved without cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 need for memory corruption at all.

That all said, a stock ASAN build -- and even more so a hypocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365tical safer-ASAN build -- provide significant mitigation potential against memory corruption vulnerabilities. One measure of how strong a mitigation is, is whecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r is totally closes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 door on a subset of bug classes or bugs. Even for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stock ASAN case, it appears that it does (linear buffer overflows for a start).

There is certainly more room for exploration in this space.

3 comments:

Michael Hicks said...

You mention handling linear buffer overflows: Does cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 protection hold when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overflow is within an allocated object? For example, if you had

struct foo {
char buf[256];
void (*f)(void);
};

My impression is that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re is no red zone between buf and f, and so no overflow will be detected, right?

Chris said...

Michael, you're right -- this would go undetected. It's solveable at compile-time by using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 "each pointer has bounds" model and creating appropriate bounds whenever something addresses into "buf".

Anonymous said...

Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r problem is custom memory allocators on top of standard malloc/new which are used by many popular packages (OpenSSL, EFL, JIT compilers, etc.). ASan by default only detects overflows in malloc buffer so overflows in intra-malloc-buffer chunks will go undetected. You can use special hooks to inform ASan about your allocator but this takes time and requires expertise.

Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r problem is overflow in struct padding area - this won't be detected as well due to design limitations.

Best regards,
Yury Gribov