UART-to-Root: The (Slightly) Harder Way

Quick Note: This post assumes some knowledge of UART and U-boot, and touches slightly on eMMC dumping.

Many familiar with hardware hacking know that UART can be a quick and easy way to find yourself with a shell on a target device. Often times, especially in older home routers and the like, you’ll be automatically logged in as root or be able to log in with an easily-guessed or default password.

In other circumstances, you may need to edit some boot arguments in the bootloader to trigger a shell (such as adding a 1 for single-user mode or adding init=/bin/sh). With this initial shell, you can dump and crack passwords or modify the firmware to grant access without the modified bootargs (change password).

Recently, I came head-to-head with a device that had a slightly more complicated boot process with many environment variables setting other environment variables that eventually called a boot script from an eMMC that did more of the same.

Some Background

My target device was being driven by a cl-som-imx6; an off-the-shelf, bolt-on System on Module from Compulab. My target version of the cl-som-imx6 utilized a 16 gig eMMC for firmware storage that had two partitions: a FAT boot partition (in addition to U-boot on an EEPROM) and an EXT4 Linux filesystem.

cl-som-imx6 with eMMC removed

My first goal for this device was to get an active shell on the device while it was fully booted. Since I had multiple copies of my target device, I went for a quick win and removed eMMC then dumped it’s contents with the hope of recovering and cracking password hashes. While I was able to get the hashes from /etc/shadow, I was disappointed to see they were hashed with sha512crypt ($6$) and have yet been unable to crack them.

Without valid credentials, my next goal was to modify boot args to bypass authentication and drop me directly into a root shell, with the hope of being able to change the password. The classic init=/bin/sh trick.

It’s important to note that when modifying the bootargs with init=/bin/sh, the device will not go through its standard boot process, therefore it will not kick off any scripts or applications that would normally fire on boot. So, while you may have a root shell, you will not be interacting with the device in its normal state. It is also temporary and will not persist after reboot.

The Problem

This is where it started getting a bit tricker. In my experience, U-boot usually has an environment variable called bootargs that passes necessary information to the kernel. In this case, there were several variables that set bootargs under different circumstances. I attempted to modify every instance where bootargs were getting set (to add init=/bin/sh) to no avail.

# binwalk part1.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
28672         0x7000          Linux kernel ARM boot executable zImage (little-endian)
34516         0x86D4          LZO compressed data
34884         0x8844          LZO compressed data
35489         0x8AA1          device tree image (dtb)
1322131       0x142C93        SHA256 hash constants, little endian
3218379       0x311BCB        mcrypt 2.5 encrypted data, algorithm: "5o", keysize: 12292 bytes, mode: "A",
3982809       0x3CC5D9        device tree image (dtb)
4273569       0x4135A1        Unix path: /var/run/L
4932888       0x4B4518        xz compressed data
5359334       0x51C6E6        LZ4 compressed data, legacy
5513216       0x542000        uImage header, header size: 64 bytes, header CRC: 0x665C5745, created: 2018-09-26 16:36:26, image size: 2397 bytes, Data Address: 0x0, Entry Point: 0x0, data CRC: 0x9F621F80, OS: Linux, CPU: ARM, image type: Script file, compression type: none, image name: "boot script"
5517312       0x543000        device tree image (dtb)
...

During this time, I also discovered that there appeared to be some sort of watch-dog active that would completely reset the device after about 2 minutes of playing around in U-boot’s menu options.

As a note: I don’t believe this was an intended “Security” function but rather an unintended effect caused by the rest of the device (attached to the cl-som-imx6) after it failed to fully boot after X time.

After an hour or so reading the UART output during boot and attempting to understand the logic flow of the environment variables, I discovered that U-boot was calling a boot script before it touched any of my edited boot args. Luckily for me, this boot script was being called from the eMMC’s boot partition, which I had dumped previously.

Binwalk quickly identified the boot script’s location, but failed to extract it. Using the offset of the script as a starting point and the offset of the following signature as the end point, I used dd to extract the script. As luck would have it, the script was actually a script (plaintext) and not a binary.

# dd if=partition1.bin of=boot.script skip=5513216 count=4096 bs=1
4096+0 records in
4096+0 records out
4096 bytes (4.1 kB, 4.0 KiB) copied, 0.0173901 s, 236 kB/s

The script was exactly 80 lines and contained several if/else statements, but most importantly, it had only one line setting the bootargs. At this point, my theory was that the only environment variables that mattered were being set by this script. I needed to modify this script to add init=/bin/sh.

# cat boot.script 
setenv loadaddr 0x10800000
setenv fdt_high 0xffffffff
setenv fdt_addr 0x15000000
setenv bootm_low 0x15000000
setenv kernel_file zImage
setenv vmalloc vmalloc=256M
setenv cma cma=384M
setenv dmfc dmfc=3
setenv console ttymxc3,115200
setenv env_addr 0x10500000
setenv env_file boot.env
setenv ext_env ext_env=empty
...
setenv setup_args 'setenv bootargs console=${console} root=${rootdev} rootfstype=ext4 rw rootwait ${ext}'
...

The next hurdle was that I didn’t have a direct way of modifying the contents of the eMMC without removing it and that’s the easy part. Getting it back on the SOM would have been tougher work than I was willing to tackle at the time.

The Solution

Without a simple way to modify the boot script, I decided to try to manually copy and paste each line of the script into the U-boot menu shell and, if necessary, remove all other environment variables.

I ran into two problems with this approach. First, any line over 34 characters that I tried to paste got truncated to 34. This was likely just caused by the ft232h buffer or something else with the serial connection. Second, and more annoying, was the watch-dog reset. There was simply no way I was going to paste in 80 lines (especially as many would require multiple c/p’s due to the 34 char limit). Even after removing as much as possible

My only answer was to automate the process. I had previously been playing around with the idea of bruteforcing simple 4 digit security codes, so I already had the outline of a script ready.

I modified the script to read lines from an input file and write them to the serial device, where any line over 32 (to be safe) characters would be chucked up. To ensure data was sent at the correct time, I put made sure the script waited for the shell prompt to return before sending the next line, with an additional .5 second sleep for good measure. Also, since the script would take over my ft232h, I needed to make sure it stopped autoboot at the correct time to enter the U-Boot shell.

This approach worked perfectly and I was dropped into a /bin/sh shell as root. I then took control of my ft232h again so I could interact manually. With a quick passwd, I changed the root password and rebooted. As the modified environment variables didn’t persist through reboot, the device booted as normal and presented me with a login prompt. I entered my newly set password and I was in.

Serial and script output ending in shell
Changing password

I’d post a screenshot of the final successful login after full boot, but I’d have to redact too much stuff that it doesn’t make any sense.

As a note: So I could keep an eye on everything, I used a second ft232h to watch the target’s TX pin and since it echoed everything back, I could also see my script’s input. Also, the watch-dog was still in effect since the device didn’t boot as it should have, therefore I had to be quick on the passwd.

The Script

Below is the script exactly as I used it. With a touch of modification to the until1 and until2 vars, it should be useable for other targets.

#!/usr/bin/env python
# By Mike Kelly
# exfil.co
# @lixmk

import serial
import sys
import argparse
import re
from time import sleep

# Key words 
until1 = "Hit any key to stop autoboot:"
until2 = "SOM-iMX6 #"

# Read from device until prompt identifier
# Not using resp in this, but you can 
def read_until(until):
    resp = ""
    while until not in resp:
        resp += dev.read(1)
    return resp

def serialprint():
    # Get to U-Boot Shell
    read_until(until1)
    dev.write("\n")
    
    # Wait for U-Boot Prompt
    read_until(until2)
    sleep(.5)
    dev.write("\n")
    with open(infile) as f:
        lines = f.readlines()
        for line in lines:
            # Lines < 32
            if len(line) < 32:
                read_until(until2)
                sleep(.5)
                print "Short Line: "+line.rstrip("\n")
                dev.write(line)
            # Break up longer lines
            else:
                read_until(until2)
                sleep(.5)
                for chunk in re.findall('.{1,32}', line):
                    print "Long Line: "+chunk.rstrip("\n")
                    dev.write(chunk)
                    sleep(.5)
                dev.write("\n")
        print "" 
        print "Done... Got root?"
        exit()

if __name__ == '__main__':
    # Argument parsing
    parser = argparse.ArgumentParser(usage='./setenv.py -d /dev/ttyUSB0 -b 115200 -f infile.txt')
    parser.add_argument('-d', '--device', required=True, help='Serial Device path ie: /dev/ttyUSB0')
    parser.add_argument('-b', '--baud', required=True, type=int, help='Serial Baud rate')
    parser.add_argument('-f', '--infile', type=str, help="Input file")
    args = parser.parse_args()
    device = args.device
    baud = args.baud
    infile = args.infile
    
    # Configuring device
    dev = serial.Serial(device, baud, timeout=5)
    # Executing
    serialprint()

2 Mics: An over-engineered solution

Background:

My wife is the Assistant Director at Cottonwood Center for the Arts, a local non-profit art center here in Colorado Springs. As a small non-profit, everybody has many different responsibilities. One of her’s is marketing and social media. A project she has been interested in piloting is a podcast and/or video series to help introduce the public to the many things that happen at Cottonwood.

As the former producer for the Exoticliability (RIP) podcast, I figured I could help her with the technical aspects of setting up equipment and recording. As a non-profit, budget is tight especially for a pilot project, but her MBP already has Garageband and we were able to find a couple cheap USB Mics that came with stands. That should be everything she needs to get started.

The Problem:

As we were getting everything hooked up, we discovered a problem. OS X seemed to recognize both USB mics, but would only use the most recent one plugged in. After much googling and almost finding an answer, it was determined that the problem stemmed from the fact that both Mics had identical USB descriptors, including serial number.

(the following screenshot only shows one mic because I “fixed” the first one already)

The simple solution was to buy a new USB microphone from a different brand. We would pay for the new one and I’d keep one of the originals. But, where’s the fun in that?

Can I Fix it?

If I’m gonna buy a new mic, I might as well check this one out in depth. I took the microphone apart to find a pretty small little board. The mic really only had two things of interest, an SOC and an EEPROM.

The SOC was a CM6327A (datasheet), an apparently common SOC for USB mics, and the EEPROM was labeled MX24C02.

The first page of the datasheet immediately tells us that:

Serial EEPROM programming interface supports customized
VID/PID/Product string/Manufacture string for device
name changed and configuration

This led me to believe that all I should need to do is change the data stored on the EEPROM and that should be enough for OS X to recognize it as a separate device.

Yes I can.

I removed the EEPROM from the board, soldered it to a breakout board from adafruit, and placed the breakout in the Mini Pro universal programmer.

The Mini Pro software did not have an option for an “MX24C02” but it did have an “M24C02”. Figuring that EEPROMs are all pretty much the same, I went with that option. Luckily there was no read protection set so I was able to view the data with no problems.

As you can see in the screenshot above, the data on the EEPROM matches the USB descriptors as reported by OS X. All that’s needed is to change some data and it should be good. I probably only needed to change the serial number (201505), but just incase, I changed all the ASCII data, but made sure the lengths never changes.

I programmed  the changes…

and returned the EEPROM to it’s original location on the USB mic’s board. I plugged the mic back into the MBP and checked the descriptors.

And there we have it, a brand new CAD LOL1337USB Licrophone. While I don’t have a screenshot of it, I can confirm that OS X now recognizes both microphones separately and they can both be used in Garageband at the same time (after some aggregate device stuff you have to do, which is required for multiple simultaneous inputs no matter what).

In the end, this solution saved me about $50, since I had all the tools necessary already, and I got a quick blog post out of it. But I am left with a little curiosity about what I could put in all the unused space of the EEPROM and what OS X might think of it. There’s also a ton of space inside the microphone. Could probably fit a small USB hub and a rubber ducky, but that’s a post for another day.

HST Rockville: eBay to DA

Below are a bunch of links related to my talk Wednesday at https://HardwareSecurity.Training in Rockville, MD. Please pardon any typos, this post has not been reviewed.

The single more important part: https://ebay.com
Concierge “toolkit”, Exploits for door controllers: https://github.com/lixmk/Concierge
eMMC Reader Device: https://smile.amazon.com/dp/B071R2STNQ/
Skype for Business Timing Attack: https://www.trustedsec.com/2017/08/attacking-self-hosted-skype-businessmicrosoft-lync-installations/
Kerberoasting pt1: https://room362.com/post/2016/kerberoast-pt1/
Kerberoasting pt2: https://room362.com/post/2016/kerberoast-pt2/
Kerberoasting pt3: https://room362.com/post/2016/kerberoast-pt3/

I “deleted” a few slides, mostly due to time, that covered interesting hardware attacks that have had (or will have) a large impact on commercial grade/corporate/enterprise security. Links from the deleted slides are included below with little-to-no description.

HID iClass Key Dump: https://www.openpcd.org/dl/HID-iCLASS-security.pdf
Direct Memory Access Attacks 1: https://github.com/ufrisk/pcileech
Direct Memory Access Attacks 2: https://github.com/carmaa/inception
A whole bunch of talks on Intel DCI and ME: