C/C++ Testing Handbook Challenge

C What's Wrong?

View the Challenges Submit Answers

How It Works

01

Read the code

Two challenges below contain real C/C++ bug classes from our Testing Handbook checklist.

02

Find the bugs

Identify the vulnerabilities and explain the issues. Stuck? The handbook chapter has hints.

03

Submit your answers

Use the form below to submit. The first 10 correct solvers win Trail of Bits swag.

The Challenges

Both challenges contain real bug classes from the C/C++ Testing Handbook checklist. If you get stuck, check out the handbook chapter for hints.

Challenge 1 Linux

The Many Quirks of Linux libc

In this simple ping program, there are two libc gotchas that make the program trivially exploitable. Can you find and explain the issues? Both bugs are covered in the Linux usermode section of the handbook.

C challenge1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>

#define ALLOWED_IP "127.3.3.1"

int main() {
    char ip_addr[128];
    struct in_addr to_ping_host, trusted_host;

    // get address
    if (!fgets(ip_addr, sizeof(ip_addr), stdin))
        return 1;
    ip_addr[strcspn(ip_addr, "\n")] = 0;

    // verify address
    if (!inet_aton(ip_addr, &to_ping_host))
        return 1;
    char *ip_addr_resolved = inet_ntoa(to_ping_host);

    // prevent SSRF
    if ((ntohl(to_ping_host.s_addr) >> 24) == 127)
        return 1;

    // only allowed
    if (!inet_aton(ALLOWED_IP, &trusted_host))
        return 1;
    char *trusted_resolved = inet_ntoa(trusted_host);

    if (strcmp(ip_addr_resolved, trusted_resolved) != 0)
        return 1;

    // ping
    char cmd[256];
    snprintf(cmd, sizeof(cmd), "ping '%s'", ip_addr);
    system(cmd);
    return 0;
}
Challenge 2 Windows

Windows Driver Registry Gotchas

This Windows Driver Framework (WDF) driver request handler queries product version values from the registry. There are several bugs here, including an easy-to-exploit denial-of-service (DoS), but one of them leads to kernel code execution by messing with the registry values. Can you figure out the bug and how to exploit it?

C challenge2.c
NTSTATUS
InitServiceCallback(
  _In_ WDFREQUEST Request
)
{
  NTSTATUS status;
  PWCHAR regPath = NULL;
  size_t bufferLength = 0;

  // fetch the product registry path from the request
  status = WdfRequestRetrieveInputBuffer(
    Request, 4, &regPath, &bufferLength);
  if (!NT_SUCCESS(status))
  {
    TraceEvents(
      TRACE_LEVEL_ERROR,
      TRACE_QUEUE,
      "%!FUNC! Failed to retrieve input buffer."
      " Status: %d", (int)status
    );
    return status;
  }
  /* check that the buffer size is a null-terminated
     Unicode (UTF-16) string of a sensible size */
  if (bufferLength < 4 ||
    bufferLength > 512 ||
    (bufferLength % 2) != 0 ||
    regPath[(bufferLength / 2) - 1] != L'\0')
  {
    TraceEvents(
      TRACE_LEVEL_ERROR,
      TRACE_QUEUE,
      "%!FUNC! Buffer length %d was incorrect.",
      (int)bufferLength
    );
    return STATUS_INVALID_PARAMETER;
  }

  ProductVersionInfo version = { 0 };
  HandlerCallback handlerCallback = NewCallback;
  int readValue = 0;
  // read the major version from the registry
  RTL_QUERY_REGISTRY_TABLE regQueryTable[2];
  RtlZeroMemory(
    regQueryTable,
    sizeof(RTL_QUERY_REGISTRY_TABLE) * 2);
  regQueryTable[0].Name = L"MajorVersion";
  regQueryTable[0].EntryContext = &readValue;
  regQueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
  regQueryTable[0].QueryRoutine = NULL;
  status = RtlQueryRegistryValues(
    RTL_REGISTRY_ABSOLUTE,
    regPath,
    regQueryTable,
    NULL,
    NULL
  );
  if (!NT_SUCCESS(status))
  {
    TraceEvents(
      TRACE_LEVEL_ERROR,
      TRACE_QUEUE,
      "%!FUNC! Failed to query registry."
      " Status: %d", (int)status
    );
    return status;
  }
  TraceEvents(
    TRACE_LEVEL_INFORMATION,
    TRACE_QUEUE,
    "%!FUNC! Major version is %d",
    (int)readValue
  );
  version.Major = readValue;
  if (version.Major < 3)
  {
    // versions prior to 3.0 need an additional check
    RtlZeroMemory(
      regQueryTable,
      sizeof(RTL_QUERY_REGISTRY_TABLE) * 2);
    regQueryTable[0].Name = L"MinorVersion";
    regQueryTable[0].EntryContext = &readValue;
    regQueryTable[0].Flags =
      RTL_QUERY_REGISTRY_DIRECT;
    regQueryTable[0].QueryRoutine = NULL;
    status = RtlQueryRegistryValues(
      RTL_REGISTRY_ABSOLUTE,
      regPath,
      regQueryTable,
      NULL,
      NULL
    );
    if (!NT_SUCCESS(status))
    {
      TraceEvents(
        TRACE_LEVEL_ERROR,
        TRACE_QUEUE,
        "%!FUNC! Failed to query registry."
        " Status: %d",
        (int)status
      );
      return status;
    }
    TraceEvents(
      TRACE_LEVEL_INFORMATION,
      TRACE_QUEUE,
      "%!FUNC! Minor version is %d",
      (int)readValue
    );
    version.Minor = readValue;
    if (!DoesVersionSupportNewCallback(version))
    {
      handlerCallback = OldCallback;
    }
  }
  SetGlobalHandlerCallback(handlerCallback);
}

Prizes

The first 10 people to correctly solve a challenge win Trail of Bits swag. All participants who submit a valid answer receive digital badges.

First 10 Solvers

Swag Pack

  • Trail of Bits apparel (t-shirt, hat, or similar)
  • Custom challenge sticker
  • Digital badges and social shoutout

Physical swag is available for winners in the United States and most European countries. Due to export regulations and shipping logistics, we cannot ship to OFAC-sanctioned countries or regions where customs duties would require recipients to pay fees. Winners in unsupported regions will receive a digital alternative.

All Valid Submissions

Digital Badges

  • Social badges for challenge participants

Leaderboard

First 10 correct solvers will appear here in real time.

# Solver Solved
1 --- ---
2 --- ---
3 --- ---
4 --- ---
5 --- ---
6 --- ---
7 --- ---
8 --- ---
9 --- ---
10 --- ---

10 swag spots remaining

Rules

Eligibility

Open to everyone. No purchase necessary.

What counts as a correct answer

Identify bug classes and explain vulnerabilities. Send us a full writeup explaining the issues and how to exploit them.

One submission per person

You may submit answers for one or both challenges. First correct submission counts.

Swag eligibility

First 10 correct solvers receive Trail of Bits apparel and a sticker pack. Sizes and shipping addresses are collected after winning. Physical swag ships to the US and most of Europe. Winners in unsupported regions receive a digital alternative.

Winner announcements

Winners are announced in real time on this page and on our social media (X and LinkedIn).

Solutions

We'll publish a follow-up blog post with full solutions after the challenge closes. Subscribe to our RSS feed to catch it.


Submit Your Answers

Fill out the form below with your analysis of the challenges. Explain the bug classes and vulnerabilities you found.

Submission form loading...


Need help securing your C/C++ systems?

Trail of Bits has reviewed C/C++ codebases for Oracle, Microsoft, Google, Discord, and dozens of other organizations. We go beyond checklists to find the bugs that matter.

Get in Touch