Background
Control
Flow Guard (CFG) was introduced in Adobe Flash Player in Windows 8.1 Update 3. But CFG-compiled flash had vulnerability
which allowed attacker to bypass this protection. Core Security Team found this
flaw and nice method how to use it
This flaw
existed because for JIT-generated code
CFG was not enabled.
On 9th of
June this vulnerability was fixed with Flash Player 18.0.0.160 by
enabling CFG for JIT-calls.
We made our analysis of this new protection and found that it is also possible to bypass it. The main goal of this research is bypass CFG and transfer control to controlled address.
Methodology
We created
new class in ActionScript - ExploitClass with few functions (func1, func2, func3) for
our experiments. In the ExploitClass function func1 calls func2, func2 calls
func3. We use three functions for simplification only, it is possible to use
only one function.
class ExploitClass {
function func1(arg1, arg2, … , argn) {
func2(arg1, arg2, …, argn);
}
function func2(arg1, arg2, … , argn) {
func3(arg1, arg2, …, argn )
}
function func3(arg1, arg2, … , argn) {
...
}
}
When we
call one JIT-generated function from another JIT-generated function call is
being checked by CFG. Every call of another JIT function in JIT-generated code looks
like this
56 push esi
50 push eax
51 push ecx
8bc8 mov ecx,eax
b840aa0f77 mov eax, LdrpValidateUserCallTarget (770faa40)
ffd0 call eax -> CFG check
59 pop ecx
58 pop eax
ffd0 call eax -> call of validated function
83c410 add esp,10h
Block of
parameters for function is being generated before CFG call and following validated
function call. Let's look at instructions, which are used for saving
parameters. They can look like this (instructions are provided with opcodes for
clarity)
8b5de0 mov ebx,dword ptr [ebp-20h]
897d88 mov dword ptr [ebp-78h],edi
8b7de8 mov edi,dword ptr [ebp-18h]
89758c mov dword ptr [ebp-74h],esi
or this
8bbde8feffff mov edi,dword ptr [ebp-118h]
898df4feffff mov dword ptr [ebp-10Ch],ecx
8b8decfeffff mov ecx,dword ptr [ebp-114h]
89b5c0feffff mov dword ptr [ebp-140h],esi
Our goal as
we mentioned earlier, to transfer control to the controlled address.
Let's
describe key points for bypassing CFG in
Flash Player 18.
1. We can
use one of the parameters of func2 as controlled address because arguments are
saved in original state without any changes in stack and (in some cases) they
are duplicated in registers.
We found
that before call eax to func2 we can save one of the parameters in ecx
register.
2. We found
that we can't transfer control to any address (for instance, in Flash.ocx) except
beginning of functions because this case is controlled by CFG. But in case of
JIT functions we can transfer control to any part of any JIT function. In our
situation it will be control transfer to the any part (any instruction) of
func2. It is wiggle room for the exploitation.
But why it
became possible?
TrendMicro's
research described generation of CFGBitmap for allocated by NtAllocVirtualMemory
executable memory (all bits will be set to '1').
It means, that this bug exists because of realization of CFG in Windows (nor in Flash Player).
3. Our idea is transfer control to any EIP-changing instruction (it can be add esp, X/ret, or as in this article - control transfer to ecx). This instruction can exist in JIT code in form of separate instruction or to be part of other instructions.
We decided
to try to find any ecx-control-transfer instruction in JIT code (for instance
call ecx, jmp ecx etc).
Let's
describe how we got our instruction. In JIT code call ecx doesn't exist
normally, but we can try to generate it via one big instruction or two separate
ones. Call ecx opcode is FF D1. We found following options in our experimental
module
1. 33FF XOR EDI,EDI
D1F8 SAR EAX,1
2. 8B8D 04FFFFFF MOV ECX,DWORD PTR [EBP-FC]
D1E1 SHL ECX,1
3. 898D 70FFFFFF MOV DWORD PTR [EBP-90],ECX
D1EB SHR EBX,1
4. 8D51 FF LEA EDX,DWORD PTR DS:[ECX-1]
D1FE SAR ESI,1
Also, we found
5. C785 00FFFFFF D1000000 MOV DWORD PTR [EBP-100],0D1
Looks interesting,
especially options with ebp-usage (2,3,5) as we know that Flash uses similar
constructions for building parameters
block. Ideal option for us is 5, because one instruction is enough in this
case.
But we
noticed important thing, that different instructions(with different opcodes)
can be used for saving parameters. For example
C785 00FFFFFF 01000000 MOV DWORD PTR [EBP-100],1
C745 8C 01000000 MOV DWORD PTR [EBP-74],1
First variation is good for us, because there is FF (we need FF in call ecx opcode). Why Flash select one type of instruction in one case and different in another one ?
If in instruction
MOV DWORD PTR [EBP-X], N
X> 0x80, first variant will be generated
X<= 0x80, second one will be generated
We can influence on X in JIT function via local variables, number of other functions calls inside JIT function, number of their parameters.
Using this information, we need to prepare func2
before call of func3 and force flash to use fist variant of opcode (we added
more local variables, additional calls etc).
We tried something like this
function func2(arg1, arg2,...,argn) {
var localvar1;
var localvar2;
....
func3(arg1, arg2,...,0xD1,..., argn )
}
in order to get MOV DWORD PTR [EBP-X],0D1 in parameters saving code. But, unfortunately,
0xD1 had been obfuscated by JIT compiler. Instead, we got something like
Seems this idea wasn't successful, but we still have two alternative ones (2,3 options) with generating of two instructions. In the end of first one will be 0xff, and in the beginning of second one will be 0xD1. We can use parameters manipulation (or local variables) for 0xFF generation and shr, shl, sar instructions for 0xD1. We tried to do something like this in ActionScript
But instead of desired
we got the same instruction, but with alternative opcode (probably because it is easier to generate it).
Ok, seems it is not our option again. But let's try to find alternatives. For instance, call [ecx]. This instruction's opcode is FF 11. When we tried following code
in modern flash exploits we can easily put address with target address for control transfer to ecx. Then, we can transfer control to ROP using standard approach.
MOV EBX,18C731Eh
XOR EBX,18C73CFh ; EBX == 0D1h
MOV DWORD PTR [EBP-X],EBX
Seems this idea wasn't successful, but we still have two alternative ones (2,3 options) with generating of two instructions. In the end of first one will be 0xff, and in the beginning of second one will be 0xD1. We can use parameters manipulation (or local variables) for 0xFF generation and shr, shl, sar instructions for 0xD1. We tried to do something like this in ActionScript
any_var1 = any_var2<<1
But instead of desired
D1E2 SHL EDX,1
we got the same instruction, but with alternative opcode (probably because it is easier to generate it).
C1E201 SHL EDX, 1
Ok, seems it is not our option again. But let's try to find alternatives. For instance, call [ecx]. This instruction's opcode is FF 11. When we tried following code
function func2(arg1, arg2 ..., argn) {
var localvar1;
var localvar2;
....
func3(arg1, arg2,...,0x11,... , argn)
}
And we got instruction
c78570ffffff11000000 mov dword ptr [ebp-90h],11h
Bingo! 0x11 is
not big number for JIT compiler and shouldn't be obfuscated. Obfuscation of
numbers begins from 0x80. Numbers less than 0x80 will not be obfuscated.
Finally, after generation of JIT func2, we have ready
call [ecx]. After exploit execution we can find address of func2 inside our
object and change it to call [ecx] 's address in func2. After all manipulations
after call func2 from func1 we will have transfer control to controlled address
inside ecx with CFG bypass.
126aed07 ff11 call dword ptr [ecx] ds:002b:42424242=????????
in modern flash exploits we can easily put address with target address for control transfer to ecx. Then, we can transfer control to ROP using standard approach.
Following
picture shows how to bypass new CFG protection in JIT code
Conclusion
Our
approach was based on three key points.
1. It is
possible to transfer control to any part of JIT-generated function despite CFG.
2. It is
possible to control one of registers in func1 and fill it with one of
parameters before call of func2.
3.
Predictability of JIT-interpreter allows us to generate interesting
control-transfer instruction.
Yurii
Drozdov, Luidmila Drozdova
(C) CVR Ltd