ENPM809V
Baremetal Security
Agenda
- Baremetal Embedded vs. Linux Embedded
- Short Introduction to ARM64E
- Walk Through of Embedded Security
What are we going to be talking about?
Embedded Systems
- Embedded systems are systems that are designed for a specific function
- They are not general purpose like our computers
- Significantly smaller in processing power, memory, and storage than general purpose computers
- Can have an operating system (generally Linux)
- Can have RTOS (real time operating systems)
- Don't have to have an operating system (baremetal)
Examples of Embedded Systems
- Medical devices
- Parts in Automotive Systems
- Engine control units, infotainment systems
- Industrial robots
- Air conditioning systems
- Digital cameras
- etc.
Embedded Linux
- Same exact kernel as general purpose Linux, but compiled to meet certain constraints
- Higher reliability
- Tighter security
- System constraints
- Many distributions support it (including Ubuntu)
- The way it operates depends on how you compile it, the distribution, and the goal of the device.
RTOS (FreeRTOS)
- Small operating system designed to be fast and lightweight
- Created to only have the things you need for an operating system
- Low-overhead
- Less configurable
- Able to easily be put on microarchitectures
- Doesn't require an memory management unit as part of the hardware
Baremetal Embedded Systems
- No operating system at all!
- This means no features that operating systems bring
- Virtual Memory - You will be coding directly to physical memory
- File System Implementation
- Any user space/kernel space features
- You should do this if size of the firmware and processing power really matters.
Why do we care?
- These systems might have access to sensitive parts of a system.
- We need to ensure that they are protected
- Botnets are a real thing! Especially with internet connected embedded systems
- You are only as strong as your weakest link!
- These devices might contain sensitive information! (Video, audio, statistics, controlling a car).
What we have to keep in mind?
- This might be one part of the entire system.
- The embedded device may not do anything that an attacker can do malicious, but it might give insight
- Pivot to another device
- Persistence
- Entry vector
Challenges with Baremetal
- You will not have symbols!
- While at compile time the ELF symbols are generated, the firmware will not have it on the device generally
- Controlling the device is sensitive!
- You can break the hardware if you are not careful
- Depending on the device, you may not be able to do much. Even if you have an exploit
What if we find a vulnerability?
- Need to update! But this is challenging
- Might require physical access to the device
- Updates may never happen (not worth it to the vendor)
- Add layers of abstraction
- Insecure embedded devices shouldn't be connected to sensitive components/the internet
- Secure Boot
- Trusted Platform Module
- Binary Transformations
- etc.
Examples of Insecure Embedded Systems
- Cars - The cambus is generally known to have vulnerabilities
- Well protected through layers of abstraction
- Cheap IOT devices - Cameras, smart speakers, not from name brand
- Even name brands have issues
- Medical devices
- this is scary....
- Electrical grids
- Also scary....
How do we work with this?
No Hardware? No Problem
- We are going to be using QEMU to emulate the baremetal firmware!
- QEMU is a full system emulator
- Allows for us to run cross architecture software as well!
How do we do this?
- Go to https://www.qemu.org/download/
- Download the latest version of QEMU
- Unzip it on your virtual machine
- Install Dependencies:
-
sudo apt-get install git libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev ninja-build
-
- ./configure
- make
- sudo make install
What else do we need?
- sudo apt install gdb-multiarch
- This adds ARM support (for x86 machines)
- Nice to also have many architectural support
- Optional:
- sudo apt install gcc-arm-none-eabi gcc-aarch64-none-eabi
Running the Firmware
- Standard: qemu-system-aarch64 -M raspi0 -kernel $(KERN).img -serial null -serial stdio [-semihosting] (be careful when using -semihosting)
-
GDB: qemu-system-arm -M raspi0 -kernel kernel.img -serial null -serial stdio -S -gdb tcp::XXXX -boot c
- To connect, run gdb[-multiarch] and then do target remote localhost:XXXX
- If you have the ELF file, you can do gdb <kernel.elf> when launching GDB for symbols
Reverse Engineering
- Radare2 or Ghidra are the best for baremetal firmware!
- You will need to figure out the base address of the firmware image
- Bets way to determine this is by looking at the strings
- We will go through this when we demo
Accessing today's Challenges
- https://github.com/ENPM809V/baremetal-firmware-challenges
Basics of ARM Assembly
To simplify things, we will focus on 32 bit ARM; however, the principals are the same!
32 bit arm processors are still common as they are smaller!
What is ARM Architecture
- ARM is a RISC based architecture
- Fewer instructions than x86
- Commonly used in smaller devices (IOT, Phones, Embedded Systems, etc.)
- Becoming more popular in general purpose computers (Apple Silicon)
What does RISC mean?
- Reduced Instruction Set Computing
- Fewer instructions that CISC based architectures
- Instructions can be executed more quickly because it reduces clock cycles per isntructions
- Means that authors/compilers need to write efficient ARM assembler.
More About ARM
- ARM is a Load/Store based model
- Move instructions can't access memory directly
- It has a mode called thumb mode
- We will get into this
- More registers
- Multi-endianness (generally stick with little endian)
- Instructions are fixed length generally
- ARM Mode - 4 bytes
- Thumb mode - 2 or 4 bytes.
ARM Registers
Aarch64 (ARM64) Registers
CSPR
CSPR
ARM Vs. Thumb
- Two main states of the ARM processor
- The difference between ARM and Thumb is the instruction sets
- Not privilege level
- ARM is always 32 bits while thumb can be either 16 bit or 32 bit
- All processors support the same ARM instruction set
- Not all processors support the same thumb instruction set
- ARM supports conditional execution instructions
- Thumb instructions have a .w suffix
- To switch between states, use either BX or BLX and set/unset the reigster's least significant bit to/from 1.
ARM Instruction Set
MNEMONIC{S}{condition} {Rd}, Operand1, Operand2
-
MNEMONIC - Short name (mnemonic) of the instruction
-
{S} - An optional suffix
-
If specified, the condition flag is updated based on the result of the operaiton
-
- {condition} - Condition necessary to execute the instruction
- {Rd} - Destination register
- Operand1 - The first operand (either register or immediate value)
- Operand2 - Second operand (either register or immediate value with optional shift)
What this looks like
ADD R0, R1, R2
ADD R0, R1, #2
MOVLE R0, #5
MOV R0, R1, LSL #1
Accessing Memory
- Unlike x86_64, we cannot memory directly with the MOV instruction
- We have to use ldr and str instructions
- ldr Ra, [Rb, Offset] - Ra is the destination, Rb is the base, Offset is either a register or immediate as an offset
- ldr Ra, [Rb, Index]! - Pre-indexed value
- Ra = Rb+Index
- Rb = Rb+Index
- ldr Ra, [Rb], index - Post-indexed value
- Ra = Rb
- Rb = Rb+index
- Note: index is still an offset, not like an array offset index
Accessing Memory
- Unlike x86_64, we cannot memory directly with the MOV instruction
- We have to use ldr and str instructions
- ldr Ra, [Rb, Offset] - Ra is the destination, Rb is the base, Offset is either a register or immediate as an offset
- ldr Ra, [Rb, Index]! - Pre-indexed value
- Ra = Rb+Index
- Rb = Rb+Index
- ldr Ra, [Rb], index - Post-indexed value
- Ra = Rb
- Rb = Rb+index
- Note: index is still an offset, not like an array offset index
Accessing Memory
- Unlike x86_64, we cannot memory directly with the MOV instruction
- We have to use ldr and str instructions
- str Ra, [Rb, Offset] - Ra is the source, Rb is the base, Offset is either a register or immediate as an offset
- Rb is unmodified
- str Ra, [Rb, Index]! - Pre-indexed value
- Ra = Source
- Memory Location = Rb+Index
- Rb is modified to Rb+Index
- str Ra, [Rb, Offset] - Ra is the source, Rb is the base, Offset is either a register or immediate as an offset
- Note: index is still an offset, not like an array offset index
Accessing Memory
- Can have a shifter applied to the end as well!
- Example: str r2, [r1, r2, LSL#2]
- ldr r3, [r1], r2, LSL#2
- Can load and store multiple at a time (won't go into this right now)
Stack
- We can either have ascending or descending stack!
- Yes this is a weird concept, but this will help us when trying to access values for buffer overflows/stack based attacks/vulnerabilities.
- This can be found by looking at the load/store instructions
- Empty = Stack pointers points to the next space on the stack that is not utilized
- Full = stack points at the last thing before an empty slot
Stack
Stack
Caling Convention
- Three main parts: prologue, body, epilogue
- Prologue saves previous state
- Body is the main part of the code
- Epilogue cleans up the function and loads the previous state
Sounds almost exactly like x86 right?
Caling Convention
- Three main parts: prologue, body, epilogue
- Prologue saves previous state
- Body is the main part of the code
- Epilogue cleans up the function and loads the previous state
Sounds almost exactly like x86 right?
Caling Convention
push {r11, lr}
add r11, sp, #8
sub sp, sp, $16
mov r0, #1
mov r1, #2
bl some_func
sub sp, r11, #8
pop {r11, pc}
Caling Convention
/* azeria@labs:~$ as func.s -o func.o && gcc func.o -o func && gdb func */
.global main
main:
push {r11, lr} /* Start of the prologue. Saving Frame Pointer and LR onto the stack */
add r11, sp, #0 /* Setting up the bottom of the stack frame */
sub sp, sp, #16 /* End of the prologue. Allocating some buffer on the stack */
mov r0, #1 /* setting up local variables (a=1). This also serves as setting up the first parameter for the max function */
mov r1, #2 /* setting up local variables (b=2). This also serves as setting up the second parameter for the max function */
bl max /* Calling/branching to function max */
sub sp, r11, #0 /* Start of the epilogue. Readjusting the Stack Pointer */
pop {r11, pc} /* End of the epilogue. Restoring Frame pointer from the stack, jumping to previously saved LR via direct load into PC */
max:
push {r11} /* Start of the prologue. Saving Frame Pointer onto the stack */
add r11, sp, #0 /* Setting up the bottom of the stack frame */
sub sp, sp, #12 /* End of the prologue. Allocating some buffer on the stack */
cmp r0, r1 /* Implementation of if(a<b) */
movlt r0, r1 /* if r0 was lower than r1, store r1 into r0 */
add sp, r11, #0 /* Start of the epilogue. Readjusting the Stack Pointer */
pop {r11} /* restoring frame pointer */
bx lr /* End of the epilogue. Jumping back to main via LR register */
Caling Convention
- 32 bit ARM keeps state, parameters, return values, on the stack.
- Stack Frame = bottom of stack, Frame Pointer = top of the stack
- 64 bit ARM (aarch64) keeps first seven parameters in x0-x7 (v0-v7 for floating point)
Using Immediate Values
- Can't just use any immediate values
- After allocating bits for instruction menomic, condition codes, destination register, operand register, etc., we have 12 bits left
- Can only have 4096 different values
- Anything greater means we have to split it up into multiple parts!
Using Immediate Values
How to learn more?
https://azeria-labs.com/writing-arm-assembly-part-1/ - Seven part series
https://modexp.wordpress.com/2018/10/30/arm64-assembly/
https://medium.com/codex/reverse-engineering-bare-metal-low-level-kernel-images-with-qemu-getting-started-c705b7b14d35
https://medium.com/@ragnarsecurity/reverse-engineering-bare-metal-kernel-images-part-2-6a52a4afa3ef
https://medium.com/geekculture/reverse-engineering-bare-metal-firmware-part-3-analyzing-arm-assembly-and-exploiting-3b2dbe219f19
Demo
Class Summary
What we covered
- We went through many vulnerabilities and how they can be exploited
- ROP Chain
- Heap
- Sandboxing
- Kernel
- Embedded
- We also went over tooling for exploiting! (pwntools, GDB)
What we covered
- These are all necessary to understand how to defend systems
- Vulnerability research
- Information Security
- Penetration Testing
- You can't defend unless you understand how the exploits work!
What to do from here?
- Continue on your journey!
- The closest job that will work at this level is vulnerability research!
- Learn how to reverse engineer
- Learn how to use fuzzers
- Learn how to use SMT Solvers
- Start learning low-level software engineering!
- Learn embedded even further
- hardware (we did not go over)
- firmware/software (touched the surface)
- Develop penetration tools
- The closest job that will work at this level is vulnerability research!
What to do from here?
- To do more binary exploitation
- Practice CTFs/Pwn.college/Pwnable.kr
- Work on red teams
- Develop your own challenges!
- Gaining more experience
- Internships/networking - talk with people within the department
- Research - CMNS has many research opportunities
- Cyber Security Club - Lots of sponsors!
- Certifications
- Not many for binary exploitation, but lots for penetration testing
Do not hack anything you aren't permitted to hack! You have been warned!
My own reflections of the course
- Great first semester! Challenging but fair
- Could have made a couple of the homework easier
- Learned a little bit on how to structure the homework
- pwn.college infrastructure worked mostly well!
- What I would change
- Apply a little more fundamentals and tooling into this course until the new version of ENPM691 is rolled out
- Maybe spend some time on reverse engineering tooling?
- Remove embedded??? - Not enough time to cover Userspace, Kernelspace, and Embedded systems
- Figure out how to foster more collaboration on Discord
My own reflections of the course
- What did all of you think?
- If you don't want to say now, send it via the course evaluation
- What would you liked? What did you want to be changed from next time?
Thank you for a wonderful semester!
No class next week, will be replaced with office hours!
This week's office hours: Wednesday 5-6:30pm
ENPM809V Baremetal Embedded Security
By Ragnar Security
ENPM809V Baremetal Embedded Security
- 66