Skip to content

Flag API

The Flag API provides cryptographically secure flag generation for CTF challenges.

Overview

The flag module provides:

  • Unique flag generation per device
  • Cryptographically secure flag creation
  • Hardware ID integration
  • Timestamp-based uniqueness
  • Lab-specific salting
  • Standard CTF flag format
#include <embsec/flag.h>

Constants

#define EMBSEC_FLAG_SIZE 32  // Maximum flag content size (excluding null terminator)

Types

embsec_flag_t

typedef struct {
    char value[EMBSEC_FLAG_SIZE + 1];  // Null-terminated flag string
} embsec_flag_t;

Structure to hold a generated flag.

Flag Generation

embsec_generate_flag

void embsec_generate_flag(embsec_flag_t *flag, 
                          const uint8_t *lab_salt, 
                          size_t salt_len);

Generate a cryptographically secure flag using hardware ID, timestamp, and lab-specific salt.

Parameters:

  • flag: Pointer to flag structure to store result
  • lab_salt: Lab-specific salt data
  • salt_len: Length of salt data

Flag Format:

  • Format: EMBSEC{hash_output}
  • hash_output: First 8 bytes of SHA256(HWID || timestamp || salt) as hex
  • Total length: 23 characters (including prefix and braces)

Example:

embsec_flag_t flag;
const uint8_t salt[] = {0x42, 0x13, 0x37, 0xCA, 0xFE};

embsec_generate_flag(&flag, salt, sizeof(salt));
embsec_printf("Your flag: %s\n", flag.value);
// Output: EMBSEC{1a2b3c4d5e6f7890}

embsec_generate_flag_str

void embsec_generate_flag_str(embsec_flag_t *flag, const char *salt_str);

Generate a flag using a string salt (convenience function).

Parameters:

  • flag: Pointer to flag structure to store result
  • salt_str: Null-terminated salt string

Example:

embsec_flag_t flag;

embsec_generate_flag_str(&flag, "buffer-overflow-lab");
embsec_print_flag(&flag);

Utility Functions

embsec_get_hwid

uint32_t embsec_get_hwid(void);

Get the hardware ID unique to each device.

Returns:

  • 32-bit hardware ID
  • On TM4C: Derived from DID0 and DID1 registers
  • On QEMU: Simulated unique ID

Example:

uint32_t hwid = embsec_get_hwid();
embsec_printf("Device ID: 0x%08X\n", hwid);

embsec_get_timestamp

uint64_t embsec_get_timestamp(void);

Get current timestamp in milliseconds since boot.

Returns:

  • 64-bit timestamp
  • Used for flag uniqueness
  • Ensures different flags even with same salt

Example:

uint64_t ts = embsec_get_timestamp();
embsec_printf("Timestamp: %llu ms\n", ts);

embsec_print_flag

void embsec_print_flag(const embsec_flag_t *flag);

Print flag to console with formatting.

Parameters:

  • flag: Pointer to flag structure

Example:

embsec_flag_t flag;
embsec_generate_flag_str(&flag, "my-lab");
embsec_print_flag(&flag);
// Output: Flag: EMBSEC{1234567890abcdef}

embsec_validate_flag_format

bool embsec_validate_flag_format(const char *flag_str);

Check if a string follows the valid flag format.

Parameters:

  • flag_str: String to validate

Returns:

  • true if format is valid (EMBSEC{...})
  • false otherwise

Example:

if (embsec_validate_flag_format(user_input)) {
    embsec_printf("Valid flag format\n");
} else {
    embsec_printf("Invalid flag format\n");
}

Flag Generation Process

The flag generation process ensures uniqueness and security:

  1. Collect Entropy Sources:
  2. Hardware ID (unique per device)
  3. Current timestamp (milliseconds since boot)
  4. Lab-specific salt

  5. Hash Combination:

    data = HWID || timestamp || salt
    hash = SHA256(data)
    

  6. Format Flag:

  7. Take first 8 bytes of hash
  8. Convert to lowercase hexadecimal
  9. Wrap in EMBSEC{...} format

Common Patterns

Lab Completion Flag

void complete_lab(const char *lab_name) {
    embsec_flag_t flag;

    embsec_printf("\n=== Lab Completed! ===\n");
    embsec_printf("Congratulations on completing %s!\n", lab_name);

    // Generate unique flag for this lab
    embsec_generate_flag_str(&flag, lab_name);

    embsec_printf("\n");
    embsec_print_flag(&flag);
    embsec_printf("\n");
    embsec_printf("Submit this flag to verify completion.\n");
}

Multi-Stage Challenge

typedef struct {
    const char *stage_name;
    const char *salt_suffix;
    bool completed;
} challenge_stage_t;

challenge_stage_t stages[] = {
    { "Stage 1: Buffer Overflow", "stage1", false },
    { "Stage 2: Format String", "stage2", false },
    { "Stage 3: ROP Chain", "stage3", false }
};

void complete_stage(int stage_num) {
    if (stage_num < 0 || stage_num >= 3) return;

    stages[stage_num].completed = true;

    embsec_printf("\n%s completed!\n", stages[stage_num].stage_name);

    // Generate stage-specific flag
    char salt[64];
    snprintf(salt, sizeof(salt), "multi-%s", stages[stage_num].salt_suffix);

    embsec_flag_t flag;
    embsec_generate_flag_str(&flag, salt);
    embsec_print_flag(&flag);

    // Check if all stages completed
    bool all_complete = true;
    for (int i = 0; i < 3; i++) {
        if (!stages[i].completed) {
            all_complete = false;
            break;
        }
    }

    if (all_complete) {
        embsec_printf("\n*** All stages completed! ***\n");
        embsec_generate_flag_str(&flag, "multi-final");
        embsec_printf("Final ");
        embsec_print_flag(&flag);
    }
}

Conditional Flag Generation

void check_exploit_success(void) {
    static bool already_exploited = false;

    if (already_exploited) {
        embsec_printf("You already captured this flag!\n");
        return;
    }

    // Verify exploit conditions
    if (verify_control_flow_hijacked() && 
        verify_shellcode_executed()) {

        already_exploited = true;

        embsec_printf("\n*** EXPLOIT SUCCESSFUL! ***\n");

        embsec_flag_t flag;
        embsec_generate_flag_str(&flag, "advanced-exploit");
        embsec_print_flag(&flag);

        // Log success
        uint32_t hwid = embsec_get_hwid();
        uint64_t ts = embsec_get_timestamp();
        embsec_printf("Device: 0x%08X, Time: %llu ms\n", hwid, ts);
    }
}

Timed Challenge

void start_timed_challenge(void) {
    uint32_t start_time = embsec_millis();
    embsec_flag_t flag;

    embsec_printf("Timed challenge started!\n");
    embsec_printf("Complete the task within 60 seconds...\n");

    // Challenge logic here
    bool success = run_challenge();

    uint32_t elapsed = embsec_elapsed_ms(start_time);

    if (success) {
        if (elapsed <= 60000) {
            embsec_printf("\nSuccess in %u.%03u seconds!\n", 
                         elapsed / 1000, elapsed % 1000);

            // Add time to salt for unique flag
            char salt[64];
            snprintf(salt, sizeof(salt), "timed-%u", elapsed);
            embsec_generate_flag(&flag, (uint8_t *)salt, strlen(salt));
            embsec_print_flag(&flag);
        } else {
            embsec_printf("\nToo slow! Took %u seconds.\n", elapsed / 1000);
        }
    } else {
        embsec_printf("\nChallenge failed!\n");
    }
}

Security Considerations

Flag Uniqueness

Each flag is unique due to:

  1. Hardware ID - Different for each device
  2. Timestamp - Changes every millisecond
  3. Lab Salt - Specific to each challenge

This ensures:

  • Students can't share flags
  • Each attempt generates a different flag
  • Flags can be traced back to specific devices/times

Salt Selection

Choose salts carefully:

// GOOD - Unique per lab/stage
embsec_generate_flag_str(&flag, "lab3-buffer-overflow-2024");

// BAD - Too generic
embsec_generate_flag_str(&flag, "lab");

// GOOD - Include version/variant
char salt[64];
snprintf(salt, sizeof(salt), "crypto-lab-v%d-variant-%c", 
         version, variant);
embsec_generate_flag(&flag, (uint8_t *)salt, strlen(salt));

Flag Verification

On the server side, flags can be verified by:

  1. Parsing the flag format
  2. Checking against database of valid hardware IDs
  3. Verifying timestamp is reasonable
  4. Regenerating with known salt to confirm

Anti-Tampering

typedef struct {
    uint32_t checksum;
    embsec_flag_t flag;
    uint8_t padding[16];
} protected_flag_t;

void generate_protected_flag(protected_flag_t *pf, const char *salt) {
    // Clear structure
    memset(pf, 0, sizeof(protected_flag_t));

    // Generate flag
    embsec_generate_flag_str(&pf->flag, salt);

    // Add random padding
    embsec_random_bytes(pf->padding, sizeof(pf->padding));

    // Calculate checksum
    pf->checksum = calculate_crc32(&pf->flag, 
                                   sizeof(embsec_flag_t) + sizeof(pf->padding));
}

bool verify_protected_flag(const protected_flag_t *pf) {
    uint32_t expected = calculate_crc32(&pf->flag, 
                                       sizeof(embsec_flag_t) + sizeof(pf->padding));
    return pf->checksum == expected;
}

Best Practices

1. Use Descriptive Salts

// Include lab name and year
embsec_generate_flag_str(&flag, "2024-spring-lab4-heap-overflow");

// Include difficulty level
embsec_generate_flag_str(&flag, "advanced-rop-challenge-hard");

// Include variant for A/B testing
char salt[64];
snprintf(salt, sizeof(salt), "format-string-variant-%c", student_id % 2 ? 'A' : 'B');

2. Log Flag Generation

void generate_and_log_flag(const char *event) {
    embsec_flag_t flag;
    uint32_t hwid = embsec_get_hwid();
    uint64_t ts = embsec_get_timestamp();

    embsec_generate_flag_str(&flag, event);

    embsec_printf("[LOG] Flag generated: %s\n", flag.value);
    embsec_printf("[LOG] Event: %s, Device: 0x%08X, Time: %llu\n", 
                  event, hwid, ts);
}

3. Prevent Brute Force

static uint32_t last_attempt = 0;
static int attempt_count = 0;

bool check_answer(uint32_t answer) {
    // Rate limiting
    if (embsec_elapsed_ms(last_attempt) < 1000) {
        attempt_count++;
        if (attempt_count > 5) {
            embsec_printf("Too many attempts! Wait 30 seconds.\n");
            embsec_delay_ms(30000);
            attempt_count = 0;
        }
    } else {
        attempt_count = 0;
    }
    last_attempt = embsec_millis();

    if (answer == CORRECT_ANSWER) {
        embsec_flag_t flag;
        embsec_generate_flag_str(&flag, "brute-force-challenge");
        embsec_print_flag(&flag);
        return true;
    }

    return false;
}

Example: Complete Flag System

#include <embsec/embsec.h>
#include <embsec/uart.h>
#include <embsec/flag.h>
#include <embsec/crypto.h>
#include <string.h>

// Challenge state
typedef struct {
    char name[32];
    bool completed;
    uint32_t score;
    uint64_t completion_time;
} challenge_t;

#define NUM_CHALLENGES 5

challenge_t challenges[NUM_CHALLENGES] = {
    { "Basic Buffer Overflow", false, 100, 0 },
    { "Format String Attack", false, 200, 0 },
    { "Integer Overflow", false, 150, 0 },
    { "Heap Exploitation", false, 300, 0 },
    { "ROP Chain", false, 400, 0 }
};

void print_scoreboard(void) {
    uint32_t total_score = 0;
    int completed = 0;

    embsec_printf("\n=== SCOREBOARD ===\n");
    for (int i = 0; i < NUM_CHALLENGES; i++) {
        embsec_printf("%d. %-25s [%3d pts] %s\n",
                      i + 1,
                      challenges[i].name,
                      challenges[i].score,
                      challenges[i].completed ? "COMPLETED" : "");

        if (challenges[i].completed) {
            total_score += challenges[i].score;
            completed++;
        }
    }

    embsec_printf("\nTotal Score: %d/%d\n", total_score, 1150);
    embsec_printf("Completed: %d/%d\n", completed, NUM_CHALLENGES);
}

void complete_challenge(int index) {
    if (index < 0 || index >= NUM_CHALLENGES) {
        return;
    }

    if (challenges[index].completed) {
        embsec_printf("You already completed this challenge!\n");
        return;
    }

    challenges[index].completed = true;
    challenges[index].completion_time = embsec_get_timestamp();

    embsec_printf("\n*** CHALLENGE COMPLETED! ***\n");
    embsec_printf("Challenge: %s\n", challenges[index].name);
    embsec_printf("Points earned: %d\n", challenges[index].score);

    // Generate unique flag for this challenge
    char salt[64];
    snprintf(salt, sizeof(salt), "ctf2024-%s-%d", 
             challenges[index].name, index);

    embsec_flag_t flag;
    embsec_generate_flag(&flag, (uint8_t *)salt, strlen(salt));

    embsec_printf("\n");
    embsec_print_flag(&flag);
    embsec_printf("\n");

    // Check for all completed
    bool all_done = true;
    for (int i = 0; i < NUM_CHALLENGES; i++) {
        if (!challenges[i].completed) {
            all_done = false;
            break;
        }
    }

    if (all_done) {
        embsec_printf("\n***** ALL CHALLENGES COMPLETED! *****\n");

        // Generate master flag
        uint8_t master_salt[64];
        int salt_len = 0;

        // Combine all completion times as salt
        for (int i = 0; i < NUM_CHALLENGES; i++) {
            memcpy(master_salt + salt_len, 
                   &challenges[i].completion_time, 
                   sizeof(uint64_t));
            salt_len += sizeof(uint64_t);
        }

        embsec_generate_flag(&flag, master_salt, salt_len);
        embsec_printf("\nMaster ");
        embsec_print_flag(&flag);
        embsec_printf("\n");

        // Calculate total time
        uint64_t total_time = embsec_get_timestamp();
        embsec_printf("\nTotal completion time: %llu.%03llu seconds\n",
                      total_time / 1000, total_time % 1000);
    }
}

// Simulated vulnerability functions
void buffer_overflow_challenge(void) {
    char buffer[32];
    volatile int exploited = 0;

    embsec_printf("\nEnter your exploit string: ");
    embsec_gets(buffer, 128);  // Intentional overflow!

    if (exploited == 0x41414141) {
        complete_challenge(0);
    } else {
        embsec_printf("Try again!\n");
    }
}

void format_string_challenge(void) {
    char buffer[256];
    volatile uint32_t secret = 0x12345678;

    embsec_printf("\nEnter format string: ");
    embsec_gets(buffer, sizeof(buffer));

    embsec_printf(buffer);  // Intentional format string bug!
    embsec_printf("\n");

    if (secret != 0x12345678) {
        complete_challenge(1);
    }
}

int main(void) {
    embsec_init();

    embsec_printf("\n=== EmbSec CTF Challenge ===\n");
    embsec_printf("Complete all challenges to win!\n\n");

    print_scoreboard();

    char cmd[32];

    while (1) {
        embsec_printf("\n> ");

        if (embsec_gets(cmd, sizeof(cmd))) {
            if (strcmp(cmd, "help") == 0) {
                embsec_printf("Commands:\n");
                embsec_printf("  help      - Show this help\n");
                embsec_printf("  score     - Show scoreboard\n");
                embsec_printf("  challenge N - Start challenge N\n");
                embsec_printf("  info      - Show system info\n");
            }
            else if (strcmp(cmd, "score") == 0) {
                print_scoreboard();
            }
            else if (strncmp(cmd, "challenge ", 10) == 0) {
                int n = cmd[10] - '1';
                if (n >= 0 && n < NUM_CHALLENGES) {
                    switch (n) {
                        case 0:
                            buffer_overflow_challenge();
                            break;
                        case 1:
                            format_string_challenge();
                            break;
                        default:
                            embsec_printf("Challenge %d not implemented yet\n", n + 1);
                    }
                } else {
                    embsec_printf("Invalid challenge number\n");
                }
            }
            else if (strcmp(cmd, "info") == 0) {
                embsec_printf("Device ID: 0x%08X\n", embsec_get_hwid());
                embsec_printf("Uptime: %llu ms\n", embsec_get_timestamp());
                embsec_printf("SDK Version: %s\n", embsec_get_version());
            }
            else if (strlen(cmd) > 0) {
                embsec_printf("Unknown command: %s\n", cmd);
            }
        }
    }
}

This example demonstrates:

  • Complete CTF challenge system
  • Multiple challenge tracking
  • Unique flag generation per challenge
  • Master flag for full completion
  • Score tracking
  • Anti-cheat measures
  • Proper flag formatting and display