Sheet 1

General Information

Note: It may look like this sheet has only three tasks, while legally we have to put five tasks on each sheet. But worry not: Task 3 is actually three tasks in a trenchcoat. They are contained within a single repository, and therefore under a single task number, for technical reasons. Also, they are related, so it is fitting to group them together like this. Yet, technically, they can be solved independently of one another.

All solutions must be Python 3 scripts if not stated otherwise. If you are new to Python 3, have a look online, there are many good resources to get started such as this intro course, this advanced course, this blog post, and this slide deck.

Please keep in mind that you should:

  • read the task description carefully

  • push all your changes to the GitLab repository (main branch) before the deadline. Make also sure that the file permissions are set correctly! If you are new to Git check out this site!

  • Make sure that your solution (also) runs in the CI environment (and not just your local machine); this also means that you must install all additional packages yourself from within the solution script (see our blog post for details how to do that).

  • Make sure that the solution is an executable Python 3 script named solution (chmod +x ./solution) with a working shebang line at the top (i.e. #!/usr/bin/env python3) so that it can be executed like this: ./solution (do not name your script, Solution,, … – just solution)

  • The final solution string, and only that, must be written to stdout and could be a number, a string, a string with the format FLAG{some letters and digits here}, depending on the specific task.

  • Describe what you are doing using detailed comments for all your solution scripts! For example, use Docstrings (link) or inline comments:

    def check_input_length(input_string):
        The input string must have a length greater than 42 and must also be even.
        length = len(input_string)
        # the final check happens here
        return (length > 42) and (length % 2 == 0)

    This helps us to find out if you really understood the task and you are not just brute-forcing some solutions. Please do not leave any commented code (i.e., code that is not needed to solve the task) in your solution files!

  • Make sure that your solution executes within 10 seconds (this is a hard timeout on our server).

  • Violating any of the points above might lead to reduced final points for the specific task!

The deadline for this sheet is 2023-05-02 23:00:00 UTC

Task 1 – Key Checker (4 Points, individual Task)

The program check_key needs a valid key as an input. Find out how to provide the key to the program and how the key checking algorithm works (use a reverse engineering tool such as IDA Pro, Ghidra, radare2, or Binary Ninja, or use a debugger, such as gdb).

The final task is to write a Python script which MUST be named solution, is able to generate valid keys, and prints 500 of them to stdout (unique keys, newline-separated, no other output), i.e., your solution could MUST look like this when executed:


where keyX is a valid key.

Note: Do not brute-force all solutions by executing the binary check_key and checking its output. This is a reverse engineering task.

Task 2 – In-memory decryption (8 Points)

Have a look at the extractme executable. It decrypts a flag during runtime. Please try to extract it.

The flag is in the format FLAG{some characters here}.

Edit the provided GDB Python script to extract the flag. Only use vanilla gdb features, no extensions allowed here!

Run the solution script, which invokes gdb with your script, to verify that it prints the flag, and nothing but the flag, to stdout.


  • Try to find the flag with pwndbg first and then write the script.
  • At no point in time, the flag is completely decrypted.
  • If you need a refresher on Python gdb scripting, check out the docs

Your solution MUST look like:

$ ./solution
FLAG{some characters here}

Task 3 – Code Audit (16 Points)

In the lecture we talked about the importance of manual code audits in software security research.

At the FKIE’s Cyber Analysis and Defense department we develop many tools that assist us in our everyday work. You can get an overview by visiting our GitHub Organization.

However, some of these tools are written in memory unsafe languages. Since we take security very seriously, we hired you for a code audit of our headerParser tool.

Subtask 1 (4 points)

Check out the current master of our headerParser tool, build the binary program and give a brief answer to the following questions:

  • What is the purpose of the tool?
  • What is its attack surface and which threat model would you suggest to assume during the review? (Think of an analyst using the tool to examine an unknown malware during an incident response or our developers receiving a bug report including a reproducer binary by some unknown GitHub user.)

We have provided you with a solution template. Write your answers where indicated in the template!


  • Have a look at the real-world code audits we linked in the lecture notes if you need some inspiration for your reporting
  • You can use the following command to build the headerParser binary (with debugging information):
mkdir build
clang -o build/headerParser -Wl,-z,relro,-z,now -D_FILE_OFFSET_BITS=64 -Og -g src/headerParser.c

Subtask 2 (4 points)

Since we cannot afford to hire you for a review of the complete application, we want you to limit your scope to a review of the -exp command line option. In particular, we want you to audit the PE_parseImageExportTable function in src/pe/idp/PEImageExportTable.h:37.

Task: Find and briefly report on a memory corruption bug in the function.

Again, write your answer in the indicated spot in the solution template.


Subtask 3 (8 points)

When submitting vulnerability reports you usually also have to supply a proof of concept (POC) that triggers the bug.

Task: Edit the provided solution Python script so that it creates a file called Parsing this file with headerParser must trigger a memory corruption. Use the given PE64_x86-64_library_KernelBase.dll as base for creating your output.


  • You only have to change a few bytes in the provided file
  • It might be a good idea to use gdb to determine the offsets at which to modify the file or to verify the memory corruption
  • We will check your solution using an address sanitizer build of the application. You can make such a build yourself by adding the -fsanitize=address option to the compiler invocation; then, running it against your should look like this
# ./headerParser -exp >/dev/null
==1859980==ERROR: AddressSanitizer: ??? on address ??? at pc ??? bp 0x7fff432234b0 sp 0x7fff432234a8
READ of size ??? at ??? thread T0
    #0 0x5642???????? in PE_parseImageExportTable /src/src/pe/idp/PEImageExportTable.h:???:??
    #1 0x5642755eb8fc in parsePEHeader /src/src/pe/PEHeaderParser.h:318:13
    #2 0x5642755eb8fc in parsePEHeaderData /src/src/pe/PEHeaderParser.h:174:5
    #3 0x5642755f1b0c in parseHeader /src/src/parser.h
    #4 0x5642755f0f69 in main /src/src/headerParser.c:162:5
    #5 0x7f4352ca0d8f  (/lib/x86_64-linux-gnu/ (BuildId: 69389d485a9793dbe873f0ea2c93e02efaa9aa3d)
    #6 0x7f4352ca0e3f in __libc_start_main (/lib/x86_64-linux-gnu/ (BuildId: 69389d485a9793dbe873f0ea2c93e02efaa9aa3d)
    #7 0x5642754f4474 in _start (/src/bin/headerParser_asan+0x49474) (BuildId: 443aee965c6bc95c69fc44ed1fede917683822f2)