Thursday, October 6, 2011

WP: Safe or Not?


During the course of kernel exploitation (or some other form of runtime kernel modification), it is frequently desirable to be able to modify the contents of read-only memory. On x86, a classic trick is to leverage the WP (write-protect) bit in the CR0 register.
As described in the Intel Manuals (Volume 3A, Section 2.5):
WP        Write Protect (bit 16 of CR0) - When set, inhibits supervisor-level proce-
          dures from writing into read-only pages; when clear, allows supervisor-level
          procedures to write into read-only pages (regardless of the U/S bit setting;
          see Section 4.1.3 and Section 4.6). This flag facilitates implementation of the
          copy-on-write method of creating a new process (forking) used by operating
          systems such as UNIX.
In an exploit where code execution has been achieved and the attacker wishes to, for example, install hooks in a read-only data structure, a simple solution is to toggle this bit to 0, perform the write, and toggle it back. This technique has been well-known for years, and is not only used in rootkits but also in commercial anti-virus products (is there a difference?).
In practice, this approach works nearly all of the time. But there are some caveats to be aware of when using this trick in exploit development.

Scheduling Race

On SMP systems, there is a scheduling race that must be dealt with. In extremely unlucky circumstances, it’s possible that the current thread disables the WP bit, is scheduled out at a precise moment, is re-scheduled onto a CPU that still has the WP bit enabled, and faults when attempting to perform a write to read-only memory. Even though I’ve never seen this happen in practice, it’s easy enough to contend with. If this is being done via some mechanism where you have the capability to compile against the current kernel (e.g. a module), one correct way of addressing this is to disable preemption of the current thread while performing writes to read-only pages, as the PaX project does:
static inline unsigned long native_pax_open_kernel(void)
{
    unsigned long cr0;

    preempt_disable();
    barrier();
    cr0 = read_cr0() ^ X86_CR0_WP;
    BUG_ON(unlikely(cr0 & X86_CR0_WP));
    write_cr0(cr0);
    return cr0 ^ X86_CR0_WP;
}

static inline unsigned long native_pax_close_kernel(void)
{
    unsigned long cr0;

    cr0 = read_cr0() ^ X86_CR0_WP;
    BUG_ON(unlikely(!(cr0 & X86_CR0_WP)));
    write_cr0(cr0);
    barrier();
    preempt_enable_no_resched();
    return cr0 ^ X86_CR0_WP;
}
These two functions, pax_open_kernel() and pax_close_kernel(), are used to modify structures such as the IDT when the PAX_KERNEXEC feature is enabled.
If you’re performing these modifications in an exploit, a simpler solution is to leverage the cli (clear interrupt flag) and sti (set interrupt flag) instructions to disable interrupts entirely during the course of the writes, which prevents re-scheduling as a side effect:
.macro disable_wp
    cli
    mov eax,cr0
    and eax,0xfffeffff
    mov cr0,eax
.endm

.macro enable_wp
    mov eax,cr0
    or eax,0x10000
    mov cr0,eax
    sti
.endm

Xen

The scheduling issue can easily be worked around, but twiz mentioned to me that there may be problems doing this on Xen. When not using HAP (hardware-assisted paging), Xen handles paging by creating shadow page tables, mapping the guest’s page tables read-only, and relying on WP to cause writes to guest page tables to trap and be handled properly by the hypervisor. As a result, CR0.WP was forcibly enabled in the past. This did not apply when using HAP, where the guest has always been able to freely access CR0.
However, in 2007, Xen added support for emulating the behavior of CR0.WP in order to support software that relies on WP modification (e.g. anti-virus). When the guest faults on a non-user write to a resident page while CR0.WP=0, the faulting instruction is then emulated to allow the write to succeed. This feature is limited by the completeness of the x86 emulator, but everything except the most esoteric instructions should be emulated properly. As a result, leveraging the WP bit to write to read-only memory on Xen should not pose any problems for exploit developers.

No comments:

Post a Comment