390 likes | 612 Views
Phumbling with Phoenix Phoenix and vulnerability finding. tim.burrell@microsoft.com. The plan. What is Phoenix? Example uses An MSRC case - CreateTextRange Analysis/Demo. Phoenix. Cool name and icon :) Some documentation/support http://connect.microsoft.com/phoenix.
E N D
Phumbling with Phoenix Phoenix and vulnerability finding tim.burrell@microsoft.com
The plan • What is Phoenix? • Example uses • An MSRC case - CreateTextRange • Analysis/Demo
Phoenix • Cool name and icon :) • Some documentation/support • http://connect.microsoft.com/phoenix
Phoenix compilation O B J E C T S O U R C E CI L HIR AST MIR LIR EIR not Phx (yet) CIL Reader Type Checker MIR LowerSSA Const SSA Dest Canon Addr Modes Lower RegAlloc EH Lower Stack Alloc Frame Gen Switch Lower Block Layout Flow Opts Encode Lister C1.dll C2.exe
AST HIR MIR LIR EIR Lowering Raising IR States More Abstract Less Abstract Phases transform IR, either within a state or from one state to another. For instance, Lower transforms MIR into LIR.
void main(int argc, char** argv) { char * message; if (argc > 1) message = "Hello, World\n"; else message = "Goodbye, World\n"; printf(message); } Simple Example
Simple Example: HIR & MIR START _main(Tech) _main: (refs=1) _argc, _argv = ENTERFUNC t112 = CMP(GT) _argc, 1 CBRANCH(GT) t112, $L7, $L6 $L7: (refs=1) _message = ASSIGN &$SG1074 GOTO $L8 $L6: (refs=1) _message = ASSIGN &$SG1076 GOTO $L8 $L8: (refs=2) = CALL &_printf, _message RET 0, $L3(Tech) $L3: (refs=1) EXITFUNC $L2: (refs=0) END
Simple Example: LIR START _main(Tech) _main: (refs=1) _argc, _argv = ENTERFUNC jmp $L12 $L12: (refs=1) ENTERBODY tv112-(EFLAGS) = cmp(GT) _argc[_FP], 1 jcc(GT) tv112-(EFLAGS), $L7, $L6 $L7: (refs=1) _message[_FP] = mov &$SG1074 jmp $L8 $L6: (refs=1) _message[_FP] = mov &$SG1076 jmp $L8 $L8: (refs=2) [ESP], {ESP} = push _message[_FP] {EAX ECX EDX ...} = call &_printf, $out[ESP], {EAX ECX ...} ESP, EFLAGS = add ESP, 4 tv118-(EAX) = mov 0 jmp $L3 $L3: (refs=1) EXITBODY jmp $L13 $L13: (refs=1) EXITFUNC tv118-(EAX) $L2: (refs=0) END
Simple Example: EIR RAW DATA 00000000: 55 8B EC 51 83 7D 08 01 0F 8E 0C 00 00 00 C7 45 00000010: FC 00 00 00 00 E9 07 00 00 00 C7 45 FC 00 00 00 00000020: 00 FF 75 FC FF 15 00 00 00 00 83 C4 04 B8 00 00 00000030: 00 00 8B E5 5D C3 00 00 RELOCATIONS Symbol Symbol Offset Type Applied To Index Name -------- ---------------- ----------------- -------- ------ 00000026 DIR32 00000000 D __imp__printf 0000001D DIR32 00000000 7 $SG1076 00000011 DIR32 00000000 6 $SG1074
Use compiler information for security analysis • Useful Phoenix freebies: • Binary disassembly • Breaking code up into basic blocks • Graphing utilities, flow/call graph • Aliasing/dependencies • Simulated/symbolic execution
The plan • What is Phoenix? • Example uses • Threadsafe reference counting (COM) • Inlinedstrcpy detection • An MSRC case - CreateTextRange • Analysis/Demo
Reference counting in COM • AddRef/Release • Object freed when refcount reaches 0 • Multithreaded environment
AddRef/Releasegrepping for ‘Interlocked’ • Following function calls • Eg overloading of the ++ operator • What we miss • Inconclusive rate of 18% • False hit rate
COM and threadsafe reference-counting • “In general it is a reasonable practice to always use the slightly less efficient InterlockedIncrement/InterlockedDecrement versions as they are known to be safe in all contexts and relieve the developer from maintaining two versions of essentially the same code.” Don Box, Essential COM
Inlinedstrcpy... • Banned/deprecated APIs • Inlined unchecked strcpy-like loops • Custom terminators
Strcpy-like loops – the gory details • Find a closed path across 1 or 2 basic blocks • A read to and a write from an 8- or 16-bit register • Both the source of the read and the destination of the write must be incremented by a small amount (<=8) • No comparisons on any reg that added to/subtracted from during the loop • Other minor tricks
The plan • What is Phoenix? • An MSRC case - CreateTextRange • Demo
MS06-013 -CreateTextRange HRESULT CInput::createTextRange(IHTMLTxtRange * * ppDisp) { HRESULT hr = S_OK; CAutoRange * pAutoRange = NULL; ... if (!ppDisp) { hr = E_INVALIDARG; goto Cleanup; } if (!HasSlavePtr()) { goto Cleanup; } ... *ppDisp = pAutoRange; pAutoRange->AddRef(); Cleanup: ... return hr; }
MS06-013 -CreateTextRange HRESULT CInput::createTextRange(IHTMLTxtRange * * ppDisp) { HRESULT hr = S_OK; CAutoRange * pAutoRange = NULL; ... if (!ppDisp) { hr = E_INVALIDARG; goto Cleanup; } *ppDisp = NULL; if (!HasSlavePtr()) { hr = E_UNEXPECTED ; goto Cleanup; } ... *ppDisp = pAutoRange; pAutoRange->AddRef(); Cleanup: ... return hr; }
MS06-013 -CreateTextRange Exploitation: hr = (*pHandler)(this, pSrvProvider, pDisp, wEntry, (PROPERTYDESC_BASIC_ABSTRACT *)pDesc, wFlags, pdispparams, pvarResult); HRESULT CInput::createTextRange( IHTMLTxtRange * * ppDisp ) if (hr == S_OK && pvarResult && V_VT(pvarResult) == VT_DISPATCH && V_DISPATCH(pvarResult)) { IDispatch *pdisptemp = V_DISPATCH(pvarResult); hr = pdisptemp->QueryInterface(IID_IDispatch, (LPVOID*)&V_DISPATCH(pvarResult)); Calling un-initialized memory on the heap
MS06-013 -CreateTextRange Key characteristics: • Function has output pointer (ppDisp) • There is a [success] path that does not initialize *ppDisp
Uninitialized output pointer Functional unit flowgraph : START *ppDisp= ...; *ppDisp= ...; END
Unlink initialization nodes START *ppDisp= ...; *ppDisp= ...; END
Comments • SAL • Inference • Null-pointer derefs • ...
Null pointer issues if(ppv == NULL) { return E_POINTER; } ... START if(ppv == NULL) TRUE FALSE return E_POINTER ... END
Uninit Output Ptrrevisited START if(ppDisp==NULL) TRUE FALSE *ppDisp= ...; *ppDisp= ...; END
Delete “ppv==NULL” edges START if(ppv==NULL) TRUE FALSE *ppv= ...; *ppv= ...; END
MS06-013 -CreateTextRange HRESULT CInput::createTextRange(IHTMLTxtRange * * ppDisp) { HRESULT hr = S_OK; CAutoRange * pAutoRange = NULL; ... if (!ppDisp) { hr = E_INVALIDARG; goto Cleanup; } if (!HasSlavePtr()) { goto Cleanup; } ... *ppDisp = pAutoRange; pAutoRange->AddRef(); Cleanup: ... return hr; }
MS06-013 -CreateTextRange HRESULT CInput::createTextRange(IHTMLTxtRange * * ppDisp) { HRESULT hr = S_OK; CAutoRange * pAutoRange = NULL; ... if (!ppDisp) { hr = E_INVALIDARG; goto Cleanup; } if (!HasSlavePtr()) { goto Cleanup; } ... *ppDisp = pAutoRange; pAutoRange->AddRef(); Cleanup: ... return hr; } Initialization Validation checks Main body of fn Cleanup and return
START CreateTextRange HRESULT hr = S_OK; CAutoRange * pAutoRange = NULL; if(!ppDisp) hr = E_INVALIDARG; goto Cleanup; if(!hasSlavePtr()) *ppDisp = pAutoRange; pAutoRange->AddRef(); goto Cleanup; Cleanup: ... return hr; END
START CreateTextRange HRESULT hr = S_OK; CAutoRange * pAutoRange = NULL; if(!ppDisp) hr = E_INVALIDARG; goto Cleanup; if(!hasSlavePtr()) *ppDisp = pAutoRange; pAutoRange->AddRef(); goto Cleanup; Cleanup: ... return hr; END
START CreateTextRange HRESULT hr = S_OK; CAutoRange * pAutoRange = NULL; if(!ppDisp) hr = E_INVALIDARG; goto Cleanup; if(!hasSlavePtr()) *ppDisp = pAutoRange; pAutoRange->AddRef(); goto Cleanup; Cleanup: ... return hr; END
The plan • What is Phoenix? • An MSRC case - CreateTextRange • Demo
Detecting null-derefs START if(ppv==NULL) TRUE FALSE *ppv= ...; *ppv= ...; END
Delete “ppv!=NULL” edges START if(ppv==NULL) TRUE FALSE *ppv= ...; *ppv= ...; END
Comments • SAL • Inference • Null-pointer derefs • ...
QueryInterface(riid, ppv) • *ppv should (almost) always be initialized • Easily detectable via simple flowgraph connectedness check
Questions? • Reporting vulns: secure@microsoft.com • Email us about other stuff switech@microsoft.com