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
Header¶
Constants¶
Types¶
embsec_flag_t¶
Structure to hold a generated flag.
Flag Generation¶
embsec_generate_flag¶
Generate a cryptographically secure flag using hardware ID, timestamp, and lab-specific salt.
Parameters:
flag: Pointer to flag structure to store resultlab_salt: Lab-specific salt datasalt_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¶
Generate a flag using a string salt (convenience function).
Parameters:
flag: Pointer to flag structure to store resultsalt_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¶
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:
embsec_get_timestamp¶
Get current timestamp in milliseconds since boot.
Returns:
- 64-bit timestamp
- Used for flag uniqueness
- Ensures different flags even with same salt
Example:
embsec_print_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¶
Check if a string follows the valid flag format.
Parameters:
flag_str: String to validate
Returns:
trueif format is valid (EMBSEC{...})falseotherwise
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:
- Collect Entropy Sources:
- Hardware ID (unique per device)
- Current timestamp (milliseconds since boot)
-
Lab-specific salt
-
Hash Combination:
-
Format Flag:
- Take first 8 bytes of hash
- Convert to lowercase hexadecimal
- 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:
- Hardware ID - Different for each device
- Timestamp - Changes every millisecond
- 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:
- Parsing the flag format
- Checking against database of valid hardware IDs
- Verifying timestamp is reasonable
- 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