buddurid@CTF:~$ 

G-Cup CTF v2 | Pwn Writeups

Categories: pwn ROP ret2gets kernel

Author Pwn Writeups


G-Cup CTF is a local Tunisian CTF hosted by Securinets Insat members , in collaboration with Gotei13 and Institut francaise de Tunisie . It was a fun CTF especially with the Gaming event running in the same Venue . From an Author’s POV tho , it wasnt very fun because AI was allowed without any restrictions. “AI is part of the game now bla bla “ yeah sure STFU now . Anyways , hopefully whoever played my challs had bits of fun . Also there was a small twist at the end of the CTF , the last 2 hours , AI was banned and a new set of ‘easier’ challenges dropped in every category

Also Youssef abid , the web Author had some very fun Web-challenges , they were very AI proof midst the CTF . Feel free to check his writeups . Waiting for other authors to Post online their writeups 🙏

Warmup

int main()
{
    setup();
    struct
    {
        char buf[0x50];
        int i;
        char c;
    } x;
    x.i=0;
    printf("%p\n", &x.buf);
    while (1)
    {
        x.c = getchar();
        if (x.c == '\n')
            break;
        x.buf[x.i++] = x.c;
    }
    return 0;
}
checksec --file=main
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       FortifiableFILE
Full RELRO      Canary found      NX disabled   PIE enabled     No RPATH   No RUNPATH   42 Symbols        No    0               1          main
  • NX disabled + obvious stack BOF , easy ret2shellcode .
  • the small twist is how to bypass the canary ?
  • when we’re overtflowing , we’re corrupting the i variable , we just corrupt it to be past the canary then overwrite the saved return address without touching the canary
  • overwrite the saved return address to point to our buffer , where we write our SYSCALL(execve,"/bin/sh",NULL,NULL) shellcode

ret2?

 ret2gets checksec --file=main
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      Symbols         FORTIFY Fortified       FortifiableFILE
Partial RELRO   No canary found   NX enabled    No PIE          No RPATH   RW-RUNPATH   40 Symbols        No    0               2          main
#include <stdio.h>

void setup()
{
    setbuf(stdout, NULL);
    setbuf(stdin, NULL);
}
int main()
{
    setup();
    printf("holaaaa");
    printf(":\n");
    char buf[0x20];
    gets(buf);
    return 0x1;
}

the challenge simply demonstrates a technique called ret2gets , you better read about it here . It’s a very cool technique and it has 2 exploitation scenarios , the challenge explores the first easy one .

TLDR

  • after Gets terminates , the rdi value always points to somewhere writable writable inside libc
  • now we have the primitive of having rdi with arbitrary content . Lets call this rdi value X .
  • our ropchain can look like this gets + gets + printf + main , the first gets returns X inside rdi , the second gets gets X value as first parameter ,so we write our content in X address . then gets returns X inside rdi (which we have written arbitrary content into ) , which we pass directly to printf , thus executing a format string payload , getting some leaks , then restart and ret2libc

Java is love

Some complex Java Heap challenge , will detail this task at another time

krnel

made this challenge to be the first linux kernel challenge , it’s the absolute easiest baby kernel challenge you can ever have . To my surprise (well it wasnt very surprising) it got more solves then the first WARMUP challenge xd . ye whatever .

#define PWN_IOCTL_COPY 0x1331
long pwn_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    char stack_input[0x100];
    inp *x = (inp *)arg;
    (void)file;

    if (cmd != PWN_IOCTL_COPY)
        return -ENOTTY;

    memcpy(stack_input, x->data, x->size);

    return 0;
}
  • the IOCTL driver for the vuln driver has an obvious stack BOF , also canaries + KASLR are disabled »> kernel ropchain
  • kernel ROPchain differ than userland ropchain in the sense that after executing our logic , we need to return to userland safely , if not , the kernel will crash and we would lose our progress . We do that by swapping the context , using the swapgs instruction gadget , then executing the iret or sysret instruction , usually we use iret because it’s easier to manage
  • what should our ropchain do ? it Should somehow change our UID to 0 . For this we use the commit_creds() function which takes a struct called cred_struct which has a uid and gid … field within it , then applies those creds to the current user-process credentials . From Where do we get a good cred_struct object ? we can just create one ourselves with the prepare_kernel_cred function , which if arg1=NULL creates and returns a pointer to cred_struct with uid=0
  • the Ropchain should look like this prepare_kernel_cred + NULL + mov_rdi_rax + commit_creds + swapgs + iretq + userland_rip + saved_es+ saved_eflags + saved_rsp + saved_ss
  • well i just lied , i compiled the most recent kernel (at the time) , so prepare_kernel_cred(NULL) shouldnt work because it’s patched to fail on NULL first argument , so just do commit_creds(init_cred) , init_cred is a global root cred already inside kernel base memory , so we know it’s address with kaslr disabled
  • profit

shelllllcoding

this challenge was dropped at the SLOPLESS phase

int main()
{

    setup();

    char *code = mmap((void *)0x13371337, 0x1000, 7, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    puts("give your shellcode : ");
    read(0, (char *)code, 0x100);

    for (int i = 0; i < 0x100 - 1; i++)
    {
        if (memcmp((char *)code + i, "\x0f\x05", 2) == 0)
        {
            puts("syscall not allowed");
            exit(1);
        }
    }

    mprotect(code, 0x1000, PROT_READ | PROT_EXEC);

    ((void (*)())code)();

    return 0;

the challenge executes shellcode from the user , but it prevents the byte sequence “\x0f\x05” (syscall instruction) from being in the shellcode

  • intended solution so to use the int 0x80 instruction which uses the x86 syscall ABI , which essentially means execute a syscall as if we were in 32bit mode
  • i’m pretty sure there were other solutions , like ropchaining from within shellcode or ret2libc , or even using the sysenter instruction …