Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save michaelyxsun/c222ca05f6a02027d10332a013195ee7 to your computer and use it in GitHub Desktop.

Select an option

Save michaelyxsun/c222ca05f6a02027d10332a013195ee7 to your computer and use it in GitHub Desktop.
Writeup submission for LIT CTF 2025 pwn/impossible string challenge

LIT CTF 2025 Challenge pwn/impossible string challenge

For this challenge we are given a source file main.c, so the first thing we should generally do is to read the source code.

main.c snippet
int main() {
    char *buf = malloc(32);
    memset(buf, 0, 32);

    puts("Type a string that has 'lit' in it and does not have 'lit' in it at the same time");
    buf[read(0, buf, 32) - 1] = 0;

    int contains = 0;
    int notContains = strstr(buf, "lit") == NULL;

    for (int i = 0; i < 32 - 2; i++) {
        if (!strncmp(buf + i, "lit", 3)) contains = 1;
    }

    free(buf);

    if (contains && notContains) {
        win();
    }
    else {
        puts("Failed");
    }

    exit(0);

}

This code reads 32 characters from stdin and sets the last character to a null byte.

buf[read(0, buf, 32) - 1] = 0;

Then we define contains and notContains and we want both to be 1 (true) to call win(). We have

int notContains = strstr(buf, "lit") == NULL;

Looking at the manpage for strstr, we see that buf is expected to be a null terminated string and returns NULL when the string is not found. Notice that the read function used to read into buf has no problem reading null bytes... hmm...

To check for contains, we go through every character buf[i] of the buffer (except the last 2) and check if the 3 character substring starting at i matches "lit". Notice that we always traverse the whole buffer regardless of any null bytes.

Do we see the solution now?

The Solution

Consider the input litctf. After reading, buf will contain

0    1    2    3    4     5    6    7    8    9   ...
--------------------------------------------------------
l    i    t    c    t    \x00 \x00 \x00 \x00 \x00 ...

(there is no f because it gets replaced by a null byte)

strstr will search indices 0 to 4 while the contains loop goes through 0 to 29. If we put "lit" after index 4, we solve the challenge.

This can be done by simply putting a null byte in the input. An example string that gives us the flag is

A\x00litA

where A can be any character. buf for this string will look like below:

0    1    2    3    4    5    6    7    8    9   ...
--------------------------------------------------------
A   \x00  l    i    t   \x00 \x00 \x00 \x00 \x00 ...

strstr will search the substring "A" and not find "lit", while the loop will find "lit" at index 2.

Note that we can type a null byte in most terminal emulators by pressing ctrl+@ or ctrl+shift+2. We can also printf a null byte into a file and pipe it into netcat:

printf 'A\x00litA' >tmp && cat tmp - | nc litctf.org 31770

LIT CTF 2025 主题 pwn/impossible string challenge

本主题给我们一个名为main.c的文件,一部分列在下方。

main.c
int main() {
    char *buf = malloc(32);
    memset(buf, 0, 32);

    puts("Type a string that has 'lit' in it and does not have 'lit' in it at the same time");
    buf[read(0, buf, 32) - 1] = 0;

    int contains = 0;
    int notContains = strstr(buf, "lit") == NULL;

    for (int i = 0; i < 32 - 2; i++) {
        if (!strncmp(buf + i, "lit", 3)) contains = 1;
    }

    free(buf);

    if (contains && notContains) {
        win();
    }
    else {
        puts("Failed");
    }

    exit(0);

}

以下代码从stdin读32个字符,并把结尾字符设为空字符。

buf[read(0, buf, 32) - 1] = 0;

如果想调用win()containsnotContains都必须是1

int notContains = strstr(buf, "lit") == NULL;

strstr遇到null字符以后就会停止运行,并null之前没找到"lit"就返回NULL。注意:你输入什么,甚至null,read都会读进去。 关于contains,我们迭代buf每一个字符(除了最后两个),每次跟"lit"比一比。 注意我们不管buf里的字符是什么,不管空字符在哪里,每次都会从字符1迭代到字符29。

你能看到答案了吗

答案

如果我们输入litctf到程序里,buf里就以下方:

0    1    2    3    4     5    6    7    8    9   ...
--------------------------------------------------------
l    i    t    c    t    \x00 \x00 \x00 \x00 \x00 ...

f被设为空字符所以不存在)

strstr会查看字符0到4,并contains循环会查看字符0到29,所以我们把"lit"放在字符4的后面就能完成主题。 因此,我们就要在buf里输入一个空字符然后输入"lit"就可以了。下方就是这样一个例子。

A\x00litA

其中A能是随便一个非空的字符。buf在此例子下会像下方。

0    1    2    3    4    5    6    7    8    9   ...
--------------------------------------------------------
A   \x00  l    i    t   \x00 \x00 \x00 \x00 \x00 ...

strstr会查看buf的字串"A"并不会遇到"lit",但contains的循环会在第二个字符遇到"lit"

在大部分的虚拟终端里我们可以用ctrl+@ctrl+shift+2输入空字符。 另外还可以使用printf输入空字符到一个文件里然后用cat通过管线输入到netcat里面。

printf 'A\x00litA' >tmp && cat tmp - | nc litctf.org 31770
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment