G-Cup CTF v2 | Pwn Writeups
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
ivariable , 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
rdivalue 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 insiderdi, the secondgetsgets X value as first parameter ,so we write our content in X address . thengetsreturns 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
swapgsinstruction gadget , then executing theiretorsysretinstruction , 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_structwhich has auidandgid… field within it , then applies those creds to the current user-process credentials . From Where do we get a goodcred_structobject ? we can just create one ourselves with theprepare_kernel_credfunction , which ifarg1=NULLcreates and returns a pointer to cred_struct withuid=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 onNULLfirst argument , so just docommit_creds(init_cred), init_cred is a globalroot credalready 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 0x80instruction 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
sysenterinstruction …