sekai - pwn/learning-oop
SekaiCTF — pwn/learning-oop
Overview
This writeup is a postmortem of my attempt at the SekaiCTF pwn/learning-oop challenge, which I did with Psi Beta Rho on August 16, 2025. The challenge centered on exploiting a C++-style object with a classic memory safety bug hidden behind an “OOP” façade. I initially fixated on a straightforward buffer overflow into adjacent fields, but the real win condition likely required abusing object layout and virtual dispatch.
The goal here isn’t to present a perfect solve, but to document my reasoning, where it went wrong, and how I should have approached it.
Initial Recon
Here’s what the class layout looked roughly like:
protected:
char name[0x100];
int age;
int fullness;
int status;
Key observations I made early:
-
nameis 256 bytes (0x100) -
intfields are 4 bytes each - The fields are laid out contiguously in memory
At some point, I identified a concrete address where an object instance lived (e.g. 0x563c24b0c2d0) and assumed age would sit at base + 0x100.
This led me to the obvious idea.
My Initial Hypothesis: Simple Overflow
I assumed the vulnerability was a linear overflow:
- Overwrite
name - Spill into
age,fullness, orstatus - Manipulate game logic (“kills the pet”, etc.)
This line of thinking:
- Counting bytes manually
- Talking about
256 char + 1 charlanding insideage - Treating the object like a flat C struct
Although there was some flaws with my line of reasoning that I only realized afterwards:
- Overwriting ints rarely yields immediate control-flow
- Even if logic corruption occurs, it doesn’t naturally lead to RIP control
- The challenge name (
learning-oop) was a huge hint I underweighted
Flawed Reasoning
What I failed to fully internalize at the time:
1. The vtable exists
If this class had any virtual methods, then the real memory layout is:
[vptr]
[name (0x100 bytes)]
[age]
[fullness]
[status]
That means:
- The first 8 bytes (on x64) are a pointer to the vtable
- The vtable controls which functions get called
Which my teammate correctly noted to me that:
“pretty sure you jus forge the vtable”
That was the correct direction.
2. Overflows don’t need to go forward
At the time, I was only considering overflowing from name forward into integers.
But most likely the target is often:
- Overwriting the vptr itself
- Or overwriting data that later influences virtual calls
If there was:
- A heap-allocated object
- A
deleteor virtual method call later
Then controlling the vtable pointer gives direct control-flow hijacking.
The Likely Intended Solution and What I Should Have Done Differently
While I didn’t finish the challenge, I should’ve taken this likely intended path:
- Heap-allocated C++ object with virtual methods
- Input allows writing into
nameunsafely -
Overflow enables corruption of:
- the vtable pointer, or
- a pointer later used as a vtable
- Craft a fake vtable in controlled memory
- Trigger a virtual call or destructor
- Gain RIP control without a libc leak
This aligns perfectly with:
- Challenge title (
learning-oop) - Peer hints about forging vtables
- Absence of easy libc references
Takeaways
Although I didn’t solve it, I learned quite a lot with this being my first ctf attempt. Nonetheless, I had a good time playing with Psi Beta Rho and flirting with my discord kitten. Here’s to better success with pwn challenges.
Finally, a cute message from my 🐱:
“you are so smarrt mod”