806 words
4 minutes
Function overwrite

Write-up of the challenge “Function overwrite”#

This challenge is part of the “Binary exploitation” category and is in the hard category.

Goal of the challenge#

The objective of the challenge is just to input 2 number that will do “fun[num_1] += num_2” so in other words just add at the index num_1 the value of the num_2.

Program structure#

chall.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <wchar.h>
#include <locale.h>
#define BUFSIZE 64
#define FLAGSIZE 64
int calculate_story_score(char *story, size_t len)
{
int score = 0;
for (size_t i = 0; i < len; i++)
{
score += story[i];
}
return score;
}
void easy_checker(char *story, size_t len)
{
if (calculate_story_score(story, len) == 1337)
{
char buf[FLAGSIZE] = {0};
FILE *f = fopen("flag.txt", "r");
if (f == NULL)
{
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\n");
exit(0);
}
fgets(buf, FLAGSIZE, f); // size bound read
printf("You're 1337. Here's the flag.\n");
printf("%s\n", buf);
}
else
{
printf("You've failed this class.");
}
}
void hard_checker(char *story, size_t len)
{
if (calculate_story_score(story, len) == 13371337)
{
char buf[FLAGSIZE] = {0};
FILE *f = fopen("flag.txt", "r");
if (f == NULL)
{
printf("%s %s", "Please create 'flag.txt' in this directory with your",
"own debugging flag.\n");
exit(0);
}
fgets(buf, FLAGSIZE, f); // size bound read
printf("You're 13371337. Here's the flag.\n");
printf("%s\n", buf);
}
else
{
printf("You've failed this class.");
}
}
void (*check)(char*, size_t) = hard_checker;
int fun[10] = {0};
void vuln()
{
char story[128];
int num1, num2;
printf("Tell me a story and then I'll tell you if you're a 1337 >> ");
scanf("%127s", story);
printf("On a totally unrelated note, give me two numbers. Keep the first one less than 10.\n");
scanf("%d %d", &num1, &num2);
if (num1 < 10)
{
fun[num1] += num2;
}
check(story, strlen(story));
}
int main(int argc, char **argv)
{
setvbuf(stdout, NULL, _IONBF, 0);
// Set the gid to the effective gid
// this prevents /bin/sh from dropping the privileges
gid_t gid = getegid();
setresgid(gid, gid, gid);
vuln();
return 0;
}

Problem#

The problem I encountered is spotting the vulnerability and after a lot of research I just found out it was “outofbounds” vulnerability.

Security breach#

The vulnerability was

if (num1 < 10){
fun[num1] += num2;
}

This is bad because it does not check if the number is negative and in C we can use that to go back and access other data in memory.

Solution#

So my idea was to jump to the check pointer “void (check)(char, size_t) = hard_checker;” and then make the value to it be the value after the check in hard_checker. After some time in gdb I found out that we need an offset of +47 to jump after the check in hard_checker. I also found out the difference in addresses from the check pointer and the fun array were 40. Because fun is an array and the program is 32 bits we need to divide 40/4 and then we get 16. Since the check pointer is before the fun array we convert the 16 to -16.

# Dump of assembler code for function fun:
# 0x0804c080 <+0>: add BYTE PTR [eax],al
# 0x0804c082 <+2>: add BYTE PTR [eax],al
# 0x0804c084 <+4>: add BYTE PTR [eax],al
# 0x0804c086 <+6>: add BYTE PTR [eax],al
# 0x0804c088 <+8>: add BYTE PTR [eax],al
# 0x0804c08a <+10>: add BYTE PTR [eax],al
# 0x0804c08c <+12>: add BYTE PTR [eax],al
# 0x0804c08e <+14>: add BYTE PTR [eax],al
# 0x0804c090 <+16>: add BYTE PTR [eax],al
# 0x0804c092 <+18>: add BYTE PTR [eax],al
# 0x0804c094 <+20>: add BYTE PTR [eax],al
# 0x0804c096 <+22>: add BYTE PTR [eax],al
# 0x0804c098 <+24>: add BYTE PTR [eax],al
# 0x0804c09a <+26>: add BYTE PTR [eax],al
# 0x0804c09c <+28>: add BYTE PTR [eax],al
# 0x0804c09e <+30>: add BYTE PTR [eax],al
# 0x0804c0a0 <+32>: add BYTE PTR [eax],al
# 0x0804c0a2 <+34>: add BYTE PTR [eax],al
# 0x0804c0a4 <+36>: add BYTE PTR [eax],al
# 0x0804c0a6 <+38>: add BYTE PTR [eax],al
# End of assembler dump.
# gef➤ disas &check
# Dump of assembler code for function check:
# 0x0804c040 <+0>: ss xchg esp,eax
# 0x0804c042 <+2>: add al,0x8
# End of assembler dump.
# gef➤
# Find offset (0x0804c080 - 0x0804c040) / 4 = -16 to check
# array[check] += offset_after_check
# gef➤ disas hard_checker
# Dump of assembler code for function hard_checker:
# 0x08049436 <+0>: endbr32
# 0x0804943a <+4>: push ebp
# 0x0804943b <+5>: mov ebp,esp
# 0x0804943d <+7>: push ebx
# 0x0804943e <+8>: sub esp,0x54
# 0x08049441 <+11>: call 0x80491f0 <__x86.get_pc_thunk.bx>
# 0x08049446 <+16>: add ebx,0x2bba
# 0x0804944c <+22>: push DWORD PTR [ebp+0xc]
# 0x0804944f <+25>: push DWORD PTR [ebp+0x8]
# 0x08049452 <+28>: call 0x80492b6 <calculate_story_score>
# 0x08049457 <+33>: add esp,0x8
# 0x0804945a <+36>: cmp eax,0xcc07c9
# 0x0804945f <+41>: jne 0x8049558 <hard_checker+290>
# 0x08049465 <+47>: mov DWORD PTR [ebp-0x4c],0x0 #THIS IS AFTER CHECK
# So -16 then +47

After excuting the script we get the flag back!

Function overwrite
https://fuwari.vercel.app/posts/function-overwrite/function-overwrite/
Author
a.b.h.a
Published at
2025-08-11
License
CC BY-NC-SA 4.0