roaris / ctf-log

0 stars 0 forks source link

AlpacaHack Round 4 : Simple Flag Checker #79

Open roaris opened 1 month ago

roaris commented 1 month ago

https://alpacahack.com/challenges/simple-flag-checker

roaris commented 1 month ago
$ file checker
checker: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=c2dd390ecbb794a78e20db0151832e9dd5c6359d, for GNU/Linux 3.2.0, not stripped

$ ./checker
flag? test
Wrong...
roaris commented 1 month ago

入力文字列はfgets関数で受け取っていて、rsp+0x30に格納される fgets関数の第2引数は0x32になっていて、これには末尾のヌルバイト分も含まれるので、期待される文字列の長さは0x31(=49)であると分かる 実際、入力受け取り後のループは0x31回ループしている rbxがループ変数である ループ内の処理は、update関数の結果をmemcmp関数で検証している memcmp関数は、メモリアドレス2つと長さを受け取り、長さ分、格納されている値を比較する関数である http://www9.plala.or.jp/sgwr-t/lib/memcmp.html 一致していれば0が返る 0が返ると、test eax, eaxでZFが1になり、setz alでalが1になり、movzx eax, alでeaxが1になる 1が返ると、eaxが0になり、r12dが0になり、Wrong...が表示される

image

roaris commented 1 month ago

memcmp関数の引数を確認する

次に、update関数を見るが、とても読めるものではない(Ghidraのdecompile結果を見ても、かなり厳しい) そのため、動的解析でなんとかしようと考える

roaris commented 1 month ago

コンテスト中はgdbスクリプトで解いた main+0xafは、call _memcmpの後のtest eax, eaxのアドレスである call _memcmpが終わった後のraxの値が0であるかを確かめている gdb.execute(f'c {i}')でi回continue出来る

import gdb

characters = '_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-.$@!?{} '
flag = 'Alpaca{'

for i in range(len(flag), 0x31):
    for c in characters:
        with open('input.txt', 'w') as f:
            f.write(flag + c)

        gdb.execute('r < input.txt')
        gdb.execute('b *(main+0xaf)')
        gdb.execute('r')
        gdb.execute(f'c {i}')

        rax = gdb.parse_and_eval('$rax')

        if rax == 0:
            flag += c
            break

gdb checker -x solve.py > /dev/nullで実行して、input.txtを確認するとフラグが徐々に見えてくる このプログラムの実行にはかなりの時間がかかる(40分ぐらいだった) charactersで文字の順番を考えるのは重要 _は割と出てくるので、後ろの方に置くのは良くなく、一番前に置いた

gdb checker -x solve.py > /dev/nullはCtrl+Cで停止しないので、プロセスIDを調べてkillする必要がある

roaris commented 1 month ago

フラグはAlpaca{h4sh_4lgor1thm_1s_b4s3d_0n_MD5_4nd_keccak} update関数は、MD5とKeccak(SHA-3)を組み合わせたものということかな

roaris commented 1 month ago

https://keymoon.hatenablog.com/entry/2024/10/07/230734 今回自分はgdbスクリプトで解いたが、他にもたくさん解法があるらしい というわけで、1つずつ試してみる

roaris commented 1 month ago

ltraceを使った方法

ltraceはバイナリが行う関数呼び出しをフックして追跡し、呼ばれた関数を引数や返り値と共にstderrに吐き出してくれる便利なプログラムです。

Alpaca{に該当する部分のmemcmp関数の結果が0になっていることが分かる memcmp関数に第4引数(9)があるのが謎

$ echo Alpaca{fakeflag} | ltrace ./checker > /dev/null
__printf_chk(1, 0x55fda56d8004, 0x7fffaef39d08, 0x55fda56d9da0)           = 6
fgets("Alpaca{fakeflag}\n", 50, 0x7f86fb89f8e0)                           = 0x7fffaef39b80
memcmp(0x7fffaef39b50, 0x55fda56da020, 16, 9)                             = 0
memcmp(0x7fffaef39b50, 0x55fda56da030, 16, 9)                             = 0
memcmp(0x7fffaef39b50, 0x55fda56da040, 16, 9)                             = 0
memcmp(0x7fffaef39b50, 0x55fda56da050, 16, 9)                             = 0
memcmp(0x7fffaef39b50, 0x55fda56da060, 16, 9)                             = 0
memcmp(0x7fffaef39b50, 0x55fda56da070, 16, 9)                             = 0
memcmp(0x7fffaef39b50, 0x55fda56da080, 16, 9)                             = 0
memcmp(0x7fffaef39b50, 0x55fda56da090, 16, 9)                             = 105
memcmp(0x7fffaef39b50, 0x55fda56da0a0, 16, 9)                             = 8
memcmp(0x7fffaef39b50, 0x55fda56da0b0, 16, 9)                             = 0xffffff7d
memcmp(0x7fffaef39b50, 0x55fda56da0c0, 16, 9)                             = 64
memcmp(0x7fffaef39b50, 0x55fda56da0d0, 16, 9)                             = 41
memcmp(0x7fffaef39b50, 0x55fda56da0e0, 16, 9)                             = 0xffffff61
memcmp(0x7fffaef39b50, 0x55fda56da0f0, 16, 9)                             = 0xfffffff4
memcmp(0x7fffaef39b50, 0x55fda56da100, 16, 9)                             = 90
memcmp(0x7fffaef39b50, 0x55fda56da110, 16, 9)                             = 0xfffffffc
memcmp(0x7fffaef39b50, 0x55fda56da120, 16, 9)                             = 0xffffffac
memcmp(0x7fffaef39b50, 0x55fda56da130, 16, 9)                             = 0xffffffb7
memcmp(0x7fffaef39b50, 0x55fda56da140, 16, 9)                             = 0xffffff85
memcmp(0x7fffaef39b50, 0x55fda56da150, 16, 9)                             = 0xffffffc0
memcmp(0x7fffaef39b50, 0x55fda56da160, 16, 9)                             = 0xffffffaa
memcmp(0x7fffaef39b50, 0x55fda56da170, 16, 9)                             = 0xfffffffe
memcmp(0x7fffaef39b50, 0x55fda56da180, 16, 9)                             = 0xffffff73
memcmp(0x7fffaef39b50, 0x55fda56da190, 16, 9)                             = 0xffffffc4
memcmp(0x7fffaef39b50, 0x55fda56da1a0, 16, 9)                             = 0xffffffdc
memcmp(0x7fffaef39b50, 0x55fda56da1b0, 16, 9)                             = 0xffffffa4
memcmp(0x7fffaef39b50, 0x55fda56da1c0, 16, 9)                             = 15
memcmp(0x7fffaef39b50, 0x55fda56da1d0, 16, 9)                             = 39
memcmp(0x7fffaef39b50, 0x55fda56da1e0, 16, 9)                             = 0xffffffec
memcmp(0x7fffaef39b50, 0x55fda56da1f0, 16, 9)                             = 16
memcmp(0x7fffaef39b50, 0x55fda56da200, 16, 9)                             = 97
memcmp(0x7fffaef39b50, 0x55fda56da210, 16, 9)                             = 127
memcmp(0x7fffaef39b50, 0x55fda56da220, 16, 9)                             = 59
memcmp(0x7fffaef39b50, 0x55fda56da230, 16, 9)                             = 177
memcmp(0x7fffaef39b50, 0x55fda56da240, 16, 9)                             = 171
memcmp(0x7fffaef39b50, 0x55fda56da250, 16, 9)                             = 152
memcmp(0x7fffaef39b50, 0x55fda56da260, 16, 9)                             = 0xffffff7b
memcmp(0x7fffaef39b50, 0x55fda56da270, 16, 9)                             = 0xffffff75
memcmp(0x7fffaef39b50, 0x55fda56da280, 16, 9)                             = 0xffffffcb
memcmp(0x7fffaef39b50, 0x55fda56da290, 16, 9)                             = 0xffffffda
memcmp(0x7fffaef39b50, 0x55fda56da2a0, 16, 9)                             = 73
memcmp(0x7fffaef39b50, 0x55fda56da2b0, 16, 9)                             = 92
memcmp(0x7fffaef39b50, 0x55fda56da2c0, 16, 9)                             = 0xffffff83
memcmp(0x7fffaef39b50, 0x55fda56da2d0, 16, 9)                             = 88
memcmp(0x7fffaef39b50, 0x55fda56da2e0, 16, 9)                             = 41
memcmp(0x7fffaef39b50, 0x55fda56da2f0, 16, 9)                             = 43
memcmp(0x7fffaef39b50, 0x55fda56da300, 16, 9)                             = 65
memcmp(0x7fffaef39b50, 0x55fda56da310, 16, 9)                             = 0xfffffff3
memcmp(0x7fffaef39b50, 0x55fda56da320, 16, 9)                             = 57
puts("Wrong...")                                                          = 9
+++ exited (status 1) +++

tqdmで進捗出すの良い subprocessの使い方は https://qiita.com/caprest/items/0245a16825789b0263ad を見た

import subprocess
from tqdm import tqdm

def check(flag):
    res = subprocess.run(f'echo {flag} | ltrace ./checker', shell=True, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE)
    c = 0

    for s in res.stderr.decode('ascii').split('\n'):
        if s.startswith('memcmp'):
            if s.split('=')[1][1:] == '0':
                c += 1
            else:
                break

    return len(flag) == c

flag = 'Alpaca{'
characters = '_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-.$@!?{} '

for i in tqdm(range(0x31 - len(flag))):
    for c in characters:
        if check(flag + c):
            flag += c
            break

print(flag)
roaris commented 1 month ago

LD_PRELOADを使ってmemcmpを差し替える

LD_PRELOAD環境変数にshared objectファイルへのパスを渡すと、動的リンク時にshared object内に含まれる関数が他のどの関数よりも優先してリンクされるようになります。つまり、バイナリから呼んでいる外部の関数(ここではmemcmp)を自作の関数で上書きできるということです。

本来はlibc.so中のmemcmp関数を使っている

$ ldd checker
        linux-vdso.so.1 (0x00007fffe71c3000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007efd5c59b000)
        /lib64/ld-linux-x86-64.so.2 (0x00007efd5c7a3000)

hook.c

int memcmp(char *buf1, char *buf2, int len) {
    for (int i = 0; i < len; i++) {
        if (buf1[i] < buf2[i]) return -1;
        if (buf1[i] > buf2[i]) return 1;
    }
    puts("ok");
    return 0;
}

shared objectを作成 : gcc hook.c -shared -o hook.so

LD_PRELOAD環境変数を設定して実行

$ echo Alpaca{fakeflag} | LD_PRELOAD=./hook.so ./checker
flag? ok
ok
ok
ok
ok
ok
ok
Wrong...
import subprocess
from tqdm import tqdm

def check(flag):
    res = subprocess.run(f'echo {flag} | LD_PRELOAD=./hook.so ./checker', shell=True, stdout=subprocess.PIPE)
    return res.stdout.decode('ascii').count('ok') == len(flag)

flag = 'Alpaca{'
characters = '_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-.$@!?{} '

for i in tqdm(range(0x31 - len(flag))):
    for c in characters:
        if check(flag + c):
            flag += c
            break

print(flag)
roaris commented 1 month ago

memcmpをputsに置き換える

前項では、動的リンカは関数名をもとにリンクしていると書きました。これはつまり、リンクに用いられている文字列を他の文字列に置き換えれば、別の関数がリンクされるようになるということです。

バイナリ中のmemcmpをputs\00\00に置き換える

import subprocess

res = open("checker", "rb").read()
open("checker_puts", "wb").write(res.replace(b'memcmp', b'puts\x00\x00'))
subprocess.run("chmod +x checker_puts", shell=True)

IDAで確認すると確かにmemcmpがputsに置き換わっている image これによって、update関数の結果が出力されるようになる

しかし、putsではヌルバイト以降が出力されないため、正確にチェックすることが出来ない つまり、↓のtableの各行のヌルバイト以降は考慮出来ない update関数の結果が前の文字にも依存しているようなので(AlとBlを与えた時で、lのupdate関数の結果が異なる)、先頭から1文字ずつ確定させていく方法だと正しいフラグを得られない なので、dfsで全探索している

import subprocess

table = [
    0x42, 0x3C, 0xF1, 0x21, 0xA3, 0x0F, 0x05, 0xAC, 0x57, 0xFD, 0x40, 0xB0, 0xA2, 0x14, 0x01, 0x98,
    0x7D, 0x2E, 0xAD, 0x5D, 0xD6, 0x23, 0x77, 0xC1, 0x06, 0x7A, 0x50, 0xCB, 0xF8, 0xCD, 0x1D, 0xBC,
    0xA8, 0x25, 0xD2, 0xDD, 0x9B, 0x73, 0x59, 0x16, 0x0D, 0xAD, 0x28, 0x22, 0xF9, 0x10, 0x81, 0x8A,
    0x50, 0x52, 0x38, 0xAF, 0x80, 0x4F, 0x15, 0xF2, 0x9A, 0xBD, 0x06, 0x65, 0x75, 0x98, 0xA2, 0x7C,
    0x49, 0x79, 0x7E, 0xD2, 0xDC, 0xDB, 0xFE, 0xC1, 0x36, 0xA3, 0xAA, 0x97, 0x1B, 0x00, 0xD2, 0x32,
    0xBF, 0x81, 0x62, 0x0A, 0xBD, 0x10, 0x98, 0x1C, 0x8C, 0xD6, 0x33, 0x82, 0x2E, 0x93, 0x89, 0x46,
    0x0F, 0x2E, 0xC0, 0x27, 0x82, 0x56, 0x0A, 0x1F, 0xD4, 0xC6, 0x32, 0x49, 0x93, 0xDE, 0x02, 0x5D,
    0x90, 0x67, 0x9B, 0x06, 0x4C, 0xCB, 0x78, 0x0A, 0x6F, 0xF3, 0x9C, 0x0E, 0x5E, 0x8E, 0x3A, 0xD6,
    0x93, 0x6F, 0x9C, 0xA1, 0x54, 0x8F, 0xC5, 0x0E, 0x48, 0x1B, 0x81, 0x31, 0xA6, 0x47, 0xD9, 0xE3,
    0xC9, 0xCD, 0xA1, 0x7E, 0xAF, 0x46, 0x9A, 0xBC, 0xAD, 0x1C, 0xC8, 0xC0, 0x8C, 0x9D, 0xD9, 0x16,
    0x9B, 0xD2, 0x22, 0x82, 0x35, 0xC3, 0xB0, 0x8F, 0x5A, 0x05, 0x55, 0x58, 0x45, 0xC5, 0xEB, 0x67,
    0x0B, 0x17, 0xD6, 0xA2, 0x20, 0x8B, 0x6B, 0xB3, 0x1B, 0xB8, 0x88, 0x6F, 0xDD, 0x0C, 0x1C, 0x8F,
    0xE9, 0x5E, 0x28, 0xB3, 0x05, 0x4B, 0x52, 0x9B, 0x83, 0xFA, 0xBA, 0x1A, 0xBD, 0x5D, 0x7E, 0xD5,
    0xDD, 0x95, 0x7F, 0x8D, 0x26, 0xA0, 0xFB, 0x1A, 0xE5, 0x30, 0xAD, 0xAA, 0x01, 0xB7, 0x9E, 0x1E,
    0x10, 0xFA, 0xAC, 0x9C, 0x72, 0x57, 0x2A, 0x7B, 0xE3, 0xC6, 0x1B, 0x8B, 0xED, 0xFD, 0x98, 0xBF,
    0xE1, 0x4F, 0x56, 0x87, 0x7E, 0x8F, 0xD9, 0xBA, 0xD6, 0x5C, 0x79, 0x4A, 0x43, 0x57, 0x0C, 0x80,
    0xB0, 0x19, 0xD8, 0x42, 0x6B, 0x2F, 0x25, 0xDF, 0x59, 0xAA, 0xDD, 0x54, 0x12, 0x35, 0xA9, 0x18,
    0x8A, 0xAC, 0x3F, 0x6A, 0x22, 0x6E, 0x44, 0x7D, 0x13, 0x7D, 0x4B, 0x18, 0x5D, 0xD3, 0xC1, 0x86,
    0x86, 0xD5, 0xB6, 0xF4, 0x47, 0x9B, 0x26, 0xC2, 0xE3, 0xA4, 0xE0, 0x12, 0x9E, 0xCF, 0x12, 0xF5,
    0xED, 0xFC, 0x70, 0x4D, 0xFF, 0xB9, 0x2C, 0x0A, 0xBF, 0xAA, 0x86, 0x4D, 0x07, 0xFF, 0x22, 0x9E,
    0x9A, 0xD2, 0x87, 0xE3, 0x66, 0x15, 0xB4, 0x19, 0x27, 0xD1, 0xEB, 0xB1, 0x76, 0xE8, 0x48, 0x12,
    0x59, 0xAF, 0xF1, 0x82, 0x9B, 0x31, 0x92, 0xE6, 0xDD, 0x80, 0x23, 0x3A, 0x3F, 0x9C, 0xD6, 0x82,
    0x99, 0xF3, 0x98, 0x60, 0x8F, 0xD0, 0x39, 0xD5, 0x9C, 0x97, 0x0F, 0xEF, 0xE6, 0x8C, 0xF8, 0x42,
    0x7A, 0x9D, 0xA9, 0x51, 0x89, 0x13, 0xEE, 0x7B, 0x47, 0x30, 0xFA, 0x14, 0x86, 0x2F, 0x1A, 0xD8,
    0xDD, 0x09, 0x78, 0xB5, 0x9C, 0x41, 0xC9, 0x62, 0x7D, 0x0F, 0xC8, 0x73, 0x42, 0x91, 0x63, 0x07,
    0xED, 0xF9, 0x23, 0xB8, 0x73, 0x7F, 0xC1, 0xC6, 0xF6, 0xF6, 0x9F, 0x01, 0xD5, 0xA7, 0x29, 0x44,
    0x43, 0x43, 0x02, 0x52, 0x2C, 0xB5, 0x2F, 0xAF, 0x55, 0x63, 0x48, 0x7B, 0x55, 0x9D, 0x45, 0xD9,
    0x94, 0x52, 0x64, 0x6B, 0xD3, 0x1B, 0xB6, 0xFC, 0x10, 0xAF, 0xEA, 0x55, 0x9A, 0xEC, 0x00, 0xFA,
    0x5A, 0x0E, 0x1C, 0xEF, 0x10, 0x9A, 0x7F, 0xD2, 0xAB, 0x20, 0xCD, 0x8D, 0x8D, 0xCF, 0x88, 0x33,
    0x10, 0xE4, 0x11, 0xAD, 0xE1, 0x7F, 0x7C, 0x59, 0x7F, 0x29, 0xF6, 0xBB, 0x32, 0x0E, 0xA8, 0xD9,
    0x7B, 0xFA, 0x37, 0xA0, 0x41, 0xB3, 0x12, 0xA4, 0xD6, 0x30, 0xE6, 0xD9, 0x7B, 0x1A, 0xDB, 0x53,
    0x2A, 0xCC, 0x13, 0x23, 0xEA, 0x09, 0xDA, 0x46, 0x5D, 0xF0, 0x25, 0x46, 0x96, 0xD3, 0x64, 0xD7,
    0x98, 0x8E, 0x85, 0x8C, 0xF4, 0xFD, 0x85, 0x12, 0xC3, 0x00, 0x6D, 0x0B, 0xD1, 0xFF, 0xE3, 0xB6,
    0x10, 0x35, 0x8B, 0x36, 0x48, 0x5B, 0x7C, 0x69, 0x77, 0xFB, 0x15, 0x65, 0xF1, 0xA5, 0x15, 0xC4,
    0x07, 0x2C, 0xD5, 0x81, 0xB2, 0x1F, 0x25, 0x20, 0x6F, 0x48, 0xE0, 0x4C, 0x24, 0x54, 0xE9, 0xC2,
    0x00, 0x62, 0x08, 0x9D, 0x7A, 0x10, 0x32, 0xB2, 0x7C, 0xBB, 0x5A, 0x51, 0xDB, 0xBF, 0xFB, 0xE1,
    0xAF, 0x20, 0x81, 0x11, 0xCE, 0xA1, 0x9F, 0x79, 0x36, 0x2C, 0x8A, 0xB3, 0x0C, 0xF0, 0xDF, 0x38,
    0x9C, 0xC9, 0x1D, 0x25, 0xEF, 0xDF, 0x4F, 0x6C, 0xC5, 0x3B, 0x74, 0x6F, 0x9D, 0xAD, 0x36, 0x05,
    0x67, 0xC5, 0x9A, 0xAE, 0x2D, 0x6D, 0xAA, 0xDB, 0x0E, 0xE0, 0xA6, 0x67, 0x45, 0xC5, 0x8E, 0xF8,
    0xC2, 0x1E, 0x64, 0xB8, 0x64, 0xA1, 0x46, 0x18, 0x2D, 0x4A, 0xBA, 0x67, 0x8A, 0x30, 0xB5, 0x75,
    0x05, 0x20, 0x96, 0x0D, 0xCE, 0xCC, 0xEC, 0x1F, 0x8C, 0xFD, 0xB0, 0x4F, 0xCD, 0x4C, 0x35, 0x46,
    0x35, 0x7D, 0xD1, 0xF5, 0x83, 0xC7, 0xA3, 0x2E, 0x2E, 0x33, 0x30, 0x1C, 0x92, 0xC9, 0x54, 0xAA,
    0xD2, 0x0F, 0x66, 0x51, 0x50, 0x85, 0x72, 0x6D, 0xBF, 0x8C, 0x57, 0x73, 0xA0, 0xD9, 0xBD, 0x72,
    0x75, 0x5A, 0xE1, 0x3B, 0x98, 0x06, 0x7D, 0x99, 0x4F, 0xBE, 0xA3, 0xB3, 0x63, 0xB2, 0xD0, 0x18,
    0x17, 0xE9, 0xC1, 0xEA, 0x3B, 0x90, 0x04, 0x57, 0xB4, 0x52, 0xDC, 0x28, 0x33, 0xE1, 0x71, 0x84,
    0x97, 0xA7, 0xE2, 0xAD, 0xBB, 0xBC, 0xAB, 0xBB, 0xF2, 0x17, 0x5D, 0xC8, 0xEE, 0xE5, 0x32, 0xF8,
    0x9C, 0x32, 0x27, 0x10, 0x5B, 0x20, 0x7B, 0x12, 0x46, 0x3D, 0xC2, 0x89, 0x15, 0x6F, 0xB9, 0x1B,
    0x32, 0xEE, 0x5D, 0x7A, 0x8E, 0x13, 0x33, 0x8B, 0xE5, 0x36, 0x60, 0xAA, 0x38, 0x53, 0x50, 0x74,
    0xB2, 0x86, 0x92, 0x5F, 0xA0, 0xFB, 0xC6, 0xEE, 0x0C, 0xCE, 0xDA, 0xA2, 0xFE, 0x5D, 0x35, 0xEA,
]

def check(flag):
    res = subprocess.run(f'echo {flag} | ./checker_puts', shell=True, stdout=subprocess.PIPE)
    b = list(res.stdout)
    l = len(flag)
    t = table[16*(l-1):16*l]

    for i in range(16):
        if t[i] == 0x00:
            t = t[:i]
            break

    ok = False

    for i in range(len(b) - len(t) + 1):
        if b[i:i+len(t)] == t:
            ok = True

    if not ok:
        return None

    if len(flag) == 0x31:
        return flag

    for c in characters:
        f = check(flag + c)

        if f:
            return f

    return None

flag = 'Alpaca{'
characters = '_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-.$@!?{} '
print(check(flag))
roaris commented 1 month ago

updateを自由に実行できるバイナリを作る

今回、update関数内に動作に必要な関数呼び出しは存在しません。そのため、update関数は直接別のところに持っていったとしても動きそうです。今回は、実行可能にしたstackにシェルコードのような形で関数のデータを置き、それを呼び出すという手法をとります。

executor.c update関数の出力が36バイトなのは、どうやって気付けば良いのか分からない

int main() {
    char update[] = { FUNC };
    char buf[36];
    read(0, &buf, 36);
    char c;
    read(0, &c, 1);
    ((void(*)(char*, char))update)(buf, c);
    write(1, &buf, 36);
}

update関数のサイズ(0x7B0)はIDAやobjdumpの結果から分かる 記事では0x7A9になっているが、最後のnop命令を除くと、0x7A9になる -Dは https://qiita.com/bdpumpkin/items/867851cb5df2d836f940 を見ると分かる -z execstackで、NX Bitを無効にし、スタック上に置いたコードを実行可能にする

import subprocess
from pwn import ELF

elf = ELF('./checker', checksec=False)
update = elf.read(elf.symbols['update'], 0x7B0)

subprocess.run(f'gcc -z execstack -DFUNC={','.join(map(str, update))} -o executor executor.c', shell=True)

update関数の中で36バイトの内部状態を扱っていることに気付けないと書けないと思う(どうやって気付けば良いのか分からない)

import subprocess
from tqdm import tqdm

table = [
    0x42, 0x3C, 0xF1, 0x21, 0xA3, 0x0F, 0x05, 0xAC, 0x57, 0xFD, 0x40, 0xB0, 0xA2, 0x14, 0x01, 0x98,
    0x7D, 0x2E, 0xAD, 0x5D, 0xD6, 0x23, 0x77, 0xC1, 0x06, 0x7A, 0x50, 0xCB, 0xF8, 0xCD, 0x1D, 0xBC,
    0xA8, 0x25, 0xD2, 0xDD, 0x9B, 0x73, 0x59, 0x16, 0x0D, 0xAD, 0x28, 0x22, 0xF9, 0x10, 0x81, 0x8A,
    0x50, 0x52, 0x38, 0xAF, 0x80, 0x4F, 0x15, 0xF2, 0x9A, 0xBD, 0x06, 0x65, 0x75, 0x98, 0xA2, 0x7C,
    0x49, 0x79, 0x7E, 0xD2, 0xDC, 0xDB, 0xFE, 0xC1, 0x36, 0xA3, 0xAA, 0x97, 0x1B, 0x00, 0xD2, 0x32,
    0xBF, 0x81, 0x62, 0x0A, 0xBD, 0x10, 0x98, 0x1C, 0x8C, 0xD6, 0x33, 0x82, 0x2E, 0x93, 0x89, 0x46,
    0x0F, 0x2E, 0xC0, 0x27, 0x82, 0x56, 0x0A, 0x1F, 0xD4, 0xC6, 0x32, 0x49, 0x93, 0xDE, 0x02, 0x5D,
    0x90, 0x67, 0x9B, 0x06, 0x4C, 0xCB, 0x78, 0x0A, 0x6F, 0xF3, 0x9C, 0x0E, 0x5E, 0x8E, 0x3A, 0xD6,
    0x93, 0x6F, 0x9C, 0xA1, 0x54, 0x8F, 0xC5, 0x0E, 0x48, 0x1B, 0x81, 0x31, 0xA6, 0x47, 0xD9, 0xE3,
    0xC9, 0xCD, 0xA1, 0x7E, 0xAF, 0x46, 0x9A, 0xBC, 0xAD, 0x1C, 0xC8, 0xC0, 0x8C, 0x9D, 0xD9, 0x16,
    0x9B, 0xD2, 0x22, 0x82, 0x35, 0xC3, 0xB0, 0x8F, 0x5A, 0x05, 0x55, 0x58, 0x45, 0xC5, 0xEB, 0x67,
    0x0B, 0x17, 0xD6, 0xA2, 0x20, 0x8B, 0x6B, 0xB3, 0x1B, 0xB8, 0x88, 0x6F, 0xDD, 0x0C, 0x1C, 0x8F,
    0xE9, 0x5E, 0x28, 0xB3, 0x05, 0x4B, 0x52, 0x9B, 0x83, 0xFA, 0xBA, 0x1A, 0xBD, 0x5D, 0x7E, 0xD5,
    0xDD, 0x95, 0x7F, 0x8D, 0x26, 0xA0, 0xFB, 0x1A, 0xE5, 0x30, 0xAD, 0xAA, 0x01, 0xB7, 0x9E, 0x1E,
    0x10, 0xFA, 0xAC, 0x9C, 0x72, 0x57, 0x2A, 0x7B, 0xE3, 0xC6, 0x1B, 0x8B, 0xED, 0xFD, 0x98, 0xBF,
    0xE1, 0x4F, 0x56, 0x87, 0x7E, 0x8F, 0xD9, 0xBA, 0xD6, 0x5C, 0x79, 0x4A, 0x43, 0x57, 0x0C, 0x80,
    0xB0, 0x19, 0xD8, 0x42, 0x6B, 0x2F, 0x25, 0xDF, 0x59, 0xAA, 0xDD, 0x54, 0x12, 0x35, 0xA9, 0x18,
    0x8A, 0xAC, 0x3F, 0x6A, 0x22, 0x6E, 0x44, 0x7D, 0x13, 0x7D, 0x4B, 0x18, 0x5D, 0xD3, 0xC1, 0x86,
    0x86, 0xD5, 0xB6, 0xF4, 0x47, 0x9B, 0x26, 0xC2, 0xE3, 0xA4, 0xE0, 0x12, 0x9E, 0xCF, 0x12, 0xF5,
    0xED, 0xFC, 0x70, 0x4D, 0xFF, 0xB9, 0x2C, 0x0A, 0xBF, 0xAA, 0x86, 0x4D, 0x07, 0xFF, 0x22, 0x9E,
    0x9A, 0xD2, 0x87, 0xE3, 0x66, 0x15, 0xB4, 0x19, 0x27, 0xD1, 0xEB, 0xB1, 0x76, 0xE8, 0x48, 0x12,
    0x59, 0xAF, 0xF1, 0x82, 0x9B, 0x31, 0x92, 0xE6, 0xDD, 0x80, 0x23, 0x3A, 0x3F, 0x9C, 0xD6, 0x82,
    0x99, 0xF3, 0x98, 0x60, 0x8F, 0xD0, 0x39, 0xD5, 0x9C, 0x97, 0x0F, 0xEF, 0xE6, 0x8C, 0xF8, 0x42,
    0x7A, 0x9D, 0xA9, 0x51, 0x89, 0x13, 0xEE, 0x7B, 0x47, 0x30, 0xFA, 0x14, 0x86, 0x2F, 0x1A, 0xD8,
    0xDD, 0x09, 0x78, 0xB5, 0x9C, 0x41, 0xC9, 0x62, 0x7D, 0x0F, 0xC8, 0x73, 0x42, 0x91, 0x63, 0x07,
    0xED, 0xF9, 0x23, 0xB8, 0x73, 0x7F, 0xC1, 0xC6, 0xF6, 0xF6, 0x9F, 0x01, 0xD5, 0xA7, 0x29, 0x44,
    0x43, 0x43, 0x02, 0x52, 0x2C, 0xB5, 0x2F, 0xAF, 0x55, 0x63, 0x48, 0x7B, 0x55, 0x9D, 0x45, 0xD9,
    0x94, 0x52, 0x64, 0x6B, 0xD3, 0x1B, 0xB6, 0xFC, 0x10, 0xAF, 0xEA, 0x55, 0x9A, 0xEC, 0x00, 0xFA,
    0x5A, 0x0E, 0x1C, 0xEF, 0x10, 0x9A, 0x7F, 0xD2, 0xAB, 0x20, 0xCD, 0x8D, 0x8D, 0xCF, 0x88, 0x33,
    0x10, 0xE4, 0x11, 0xAD, 0xE1, 0x7F, 0x7C, 0x59, 0x7F, 0x29, 0xF6, 0xBB, 0x32, 0x0E, 0xA8, 0xD9,
    0x7B, 0xFA, 0x37, 0xA0, 0x41, 0xB3, 0x12, 0xA4, 0xD6, 0x30, 0xE6, 0xD9, 0x7B, 0x1A, 0xDB, 0x53,
    0x2A, 0xCC, 0x13, 0x23, 0xEA, 0x09, 0xDA, 0x46, 0x5D, 0xF0, 0x25, 0x46, 0x96, 0xD3, 0x64, 0xD7,
    0x98, 0x8E, 0x85, 0x8C, 0xF4, 0xFD, 0x85, 0x12, 0xC3, 0x00, 0x6D, 0x0B, 0xD1, 0xFF, 0xE3, 0xB6,
    0x10, 0x35, 0x8B, 0x36, 0x48, 0x5B, 0x7C, 0x69, 0x77, 0xFB, 0x15, 0x65, 0xF1, 0xA5, 0x15, 0xC4,
    0x07, 0x2C, 0xD5, 0x81, 0xB2, 0x1F, 0x25, 0x20, 0x6F, 0x48, 0xE0, 0x4C, 0x24, 0x54, 0xE9, 0xC2,
    0x00, 0x62, 0x08, 0x9D, 0x7A, 0x10, 0x32, 0xB2, 0x7C, 0xBB, 0x5A, 0x51, 0xDB, 0xBF, 0xFB, 0xE1,
    0xAF, 0x20, 0x81, 0x11, 0xCE, 0xA1, 0x9F, 0x79, 0x36, 0x2C, 0x8A, 0xB3, 0x0C, 0xF0, 0xDF, 0x38,
    0x9C, 0xC9, 0x1D, 0x25, 0xEF, 0xDF, 0x4F, 0x6C, 0xC5, 0x3B, 0x74, 0x6F, 0x9D, 0xAD, 0x36, 0x05,
    0x67, 0xC5, 0x9A, 0xAE, 0x2D, 0x6D, 0xAA, 0xDB, 0x0E, 0xE0, 0xA6, 0x67, 0x45, 0xC5, 0x8E, 0xF8,
    0xC2, 0x1E, 0x64, 0xB8, 0x64, 0xA1, 0x46, 0x18, 0x2D, 0x4A, 0xBA, 0x67, 0x8A, 0x30, 0xB5, 0x75,
    0x05, 0x20, 0x96, 0x0D, 0xCE, 0xCC, 0xEC, 0x1F, 0x8C, 0xFD, 0xB0, 0x4F, 0xCD, 0x4C, 0x35, 0x46,
    0x35, 0x7D, 0xD1, 0xF5, 0x83, 0xC7, 0xA3, 0x2E, 0x2E, 0x33, 0x30, 0x1C, 0x92, 0xC9, 0x54, 0xAA,
    0xD2, 0x0F, 0x66, 0x51, 0x50, 0x85, 0x72, 0x6D, 0xBF, 0x8C, 0x57, 0x73, 0xA0, 0xD9, 0xBD, 0x72,
    0x75, 0x5A, 0xE1, 0x3B, 0x98, 0x06, 0x7D, 0x99, 0x4F, 0xBE, 0xA3, 0xB3, 0x63, 0xB2, 0xD0, 0x18,
    0x17, 0xE9, 0xC1, 0xEA, 0x3B, 0x90, 0x04, 0x57, 0xB4, 0x52, 0xDC, 0x28, 0x33, 0xE1, 0x71, 0x84,
    0x97, 0xA7, 0xE2, 0xAD, 0xBB, 0xBC, 0xAB, 0xBB, 0xF2, 0x17, 0x5D, 0xC8, 0xEE, 0xE5, 0x32, 0xF8,
    0x9C, 0x32, 0x27, 0x10, 0x5B, 0x20, 0x7B, 0x12, 0x46, 0x3D, 0xC2, 0x89, 0x15, 0x6F, 0xB9, 0x1B,
    0x32, 0xEE, 0x5D, 0x7A, 0x8E, 0x13, 0x33, 0x8B, 0xE5, 0x36, 0x60, 0xAA, 0x38, 0x53, 0x50, 0x74,
    0xB2, 0x86, 0x92, 0x5F, 0xA0, 0xFB, 0xC6, 0xEE, 0x0C, 0xCE, 0xDA, 0xA2, 0xFE, 0x5D, 0x35, 0xEA,
]

flag = ''
characters = '_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-.$@!?{} '
state = b'\x00' * 36

for i in tqdm(range(0x31)):
    for c in characters:
        res = subprocess.run(f'./executor', input=state+bytes(c, 'ascii'), shell=True, stdout=subprocess.PIPE).stdout

        if list(res[:16]) == table[16*i:16*i+16]:
            state = res
            flag += c

print(flag)
roaris commented 1 month ago

ctypes.CDLLを用いてPythonからupdateを実行する

Pythonには共有ライブラリを読み込んで、その関数をPythonから実行するためのctypesというライブラリが存在します。

shared objectを作るコード bin[lief.ELF.DynamicEntry.TAG.FLAGS_1].remove(lief.ELF.DynamicEntryFlags.FLAG.PIE)は何か分からない

import lief

bin = lief.parse("./checker")
bin.add_exported_function(bin.get_function_address("update"), "update")
bin[lief.ELF.DynamicEntry.TAG.FLAGS_1].remove(lief.ELF.DynamicEntryFlags.FLAG.PIE)
bin.write("checker.so")
import ctypes
import subprocess
from tqdm import tqdm

table = [
    0x42, 0x3C, 0xF1, 0x21, 0xA3, 0x0F, 0x05, 0xAC, 0x57, 0xFD, 0x40, 0xB0, 0xA2, 0x14, 0x01, 0x98,
    0x7D, 0x2E, 0xAD, 0x5D, 0xD6, 0x23, 0x77, 0xC1, 0x06, 0x7A, 0x50, 0xCB, 0xF8, 0xCD, 0x1D, 0xBC,
    0xA8, 0x25, 0xD2, 0xDD, 0x9B, 0x73, 0x59, 0x16, 0x0D, 0xAD, 0x28, 0x22, 0xF9, 0x10, 0x81, 0x8A,
    0x50, 0x52, 0x38, 0xAF, 0x80, 0x4F, 0x15, 0xF2, 0x9A, 0xBD, 0x06, 0x65, 0x75, 0x98, 0xA2, 0x7C,
    0x49, 0x79, 0x7E, 0xD2, 0xDC, 0xDB, 0xFE, 0xC1, 0x36, 0xA3, 0xAA, 0x97, 0x1B, 0x00, 0xD2, 0x32,
    0xBF, 0x81, 0x62, 0x0A, 0xBD, 0x10, 0x98, 0x1C, 0x8C, 0xD6, 0x33, 0x82, 0x2E, 0x93, 0x89, 0x46,
    0x0F, 0x2E, 0xC0, 0x27, 0x82, 0x56, 0x0A, 0x1F, 0xD4, 0xC6, 0x32, 0x49, 0x93, 0xDE, 0x02, 0x5D,
    0x90, 0x67, 0x9B, 0x06, 0x4C, 0xCB, 0x78, 0x0A, 0x6F, 0xF3, 0x9C, 0x0E, 0x5E, 0x8E, 0x3A, 0xD6,
    0x93, 0x6F, 0x9C, 0xA1, 0x54, 0x8F, 0xC5, 0x0E, 0x48, 0x1B, 0x81, 0x31, 0xA6, 0x47, 0xD9, 0xE3,
    0xC9, 0xCD, 0xA1, 0x7E, 0xAF, 0x46, 0x9A, 0xBC, 0xAD, 0x1C, 0xC8, 0xC0, 0x8C, 0x9D, 0xD9, 0x16,
    0x9B, 0xD2, 0x22, 0x82, 0x35, 0xC3, 0xB0, 0x8F, 0x5A, 0x05, 0x55, 0x58, 0x45, 0xC5, 0xEB, 0x67,
    0x0B, 0x17, 0xD6, 0xA2, 0x20, 0x8B, 0x6B, 0xB3, 0x1B, 0xB8, 0x88, 0x6F, 0xDD, 0x0C, 0x1C, 0x8F,
    0xE9, 0x5E, 0x28, 0xB3, 0x05, 0x4B, 0x52, 0x9B, 0x83, 0xFA, 0xBA, 0x1A, 0xBD, 0x5D, 0x7E, 0xD5,
    0xDD, 0x95, 0x7F, 0x8D, 0x26, 0xA0, 0xFB, 0x1A, 0xE5, 0x30, 0xAD, 0xAA, 0x01, 0xB7, 0x9E, 0x1E,
    0x10, 0xFA, 0xAC, 0x9C, 0x72, 0x57, 0x2A, 0x7B, 0xE3, 0xC6, 0x1B, 0x8B, 0xED, 0xFD, 0x98, 0xBF,
    0xE1, 0x4F, 0x56, 0x87, 0x7E, 0x8F, 0xD9, 0xBA, 0xD6, 0x5C, 0x79, 0x4A, 0x43, 0x57, 0x0C, 0x80,
    0xB0, 0x19, 0xD8, 0x42, 0x6B, 0x2F, 0x25, 0xDF, 0x59, 0xAA, 0xDD, 0x54, 0x12, 0x35, 0xA9, 0x18,
    0x8A, 0xAC, 0x3F, 0x6A, 0x22, 0x6E, 0x44, 0x7D, 0x13, 0x7D, 0x4B, 0x18, 0x5D, 0xD3, 0xC1, 0x86,
    0x86, 0xD5, 0xB6, 0xF4, 0x47, 0x9B, 0x26, 0xC2, 0xE3, 0xA4, 0xE0, 0x12, 0x9E, 0xCF, 0x12, 0xF5,
    0xED, 0xFC, 0x70, 0x4D, 0xFF, 0xB9, 0x2C, 0x0A, 0xBF, 0xAA, 0x86, 0x4D, 0x07, 0xFF, 0x22, 0x9E,
    0x9A, 0xD2, 0x87, 0xE3, 0x66, 0x15, 0xB4, 0x19, 0x27, 0xD1, 0xEB, 0xB1, 0x76, 0xE8, 0x48, 0x12,
    0x59, 0xAF, 0xF1, 0x82, 0x9B, 0x31, 0x92, 0xE6, 0xDD, 0x80, 0x23, 0x3A, 0x3F, 0x9C, 0xD6, 0x82,
    0x99, 0xF3, 0x98, 0x60, 0x8F, 0xD0, 0x39, 0xD5, 0x9C, 0x97, 0x0F, 0xEF, 0xE6, 0x8C, 0xF8, 0x42,
    0x7A, 0x9D, 0xA9, 0x51, 0x89, 0x13, 0xEE, 0x7B, 0x47, 0x30, 0xFA, 0x14, 0x86, 0x2F, 0x1A, 0xD8,
    0xDD, 0x09, 0x78, 0xB5, 0x9C, 0x41, 0xC9, 0x62, 0x7D, 0x0F, 0xC8, 0x73, 0x42, 0x91, 0x63, 0x07,
    0xED, 0xF9, 0x23, 0xB8, 0x73, 0x7F, 0xC1, 0xC6, 0xF6, 0xF6, 0x9F, 0x01, 0xD5, 0xA7, 0x29, 0x44,
    0x43, 0x43, 0x02, 0x52, 0x2C, 0xB5, 0x2F, 0xAF, 0x55, 0x63, 0x48, 0x7B, 0x55, 0x9D, 0x45, 0xD9,
    0x94, 0x52, 0x64, 0x6B, 0xD3, 0x1B, 0xB6, 0xFC, 0x10, 0xAF, 0xEA, 0x55, 0x9A, 0xEC, 0x00, 0xFA,
    0x5A, 0x0E, 0x1C, 0xEF, 0x10, 0x9A, 0x7F, 0xD2, 0xAB, 0x20, 0xCD, 0x8D, 0x8D, 0xCF, 0x88, 0x33,
    0x10, 0xE4, 0x11, 0xAD, 0xE1, 0x7F, 0x7C, 0x59, 0x7F, 0x29, 0xF6, 0xBB, 0x32, 0x0E, 0xA8, 0xD9,
    0x7B, 0xFA, 0x37, 0xA0, 0x41, 0xB3, 0x12, 0xA4, 0xD6, 0x30, 0xE6, 0xD9, 0x7B, 0x1A, 0xDB, 0x53,
    0x2A, 0xCC, 0x13, 0x23, 0xEA, 0x09, 0xDA, 0x46, 0x5D, 0xF0, 0x25, 0x46, 0x96, 0xD3, 0x64, 0xD7,
    0x98, 0x8E, 0x85, 0x8C, 0xF4, 0xFD, 0x85, 0x12, 0xC3, 0x00, 0x6D, 0x0B, 0xD1, 0xFF, 0xE3, 0xB6,
    0x10, 0x35, 0x8B, 0x36, 0x48, 0x5B, 0x7C, 0x69, 0x77, 0xFB, 0x15, 0x65, 0xF1, 0xA5, 0x15, 0xC4,
    0x07, 0x2C, 0xD5, 0x81, 0xB2, 0x1F, 0x25, 0x20, 0x6F, 0x48, 0xE0, 0x4C, 0x24, 0x54, 0xE9, 0xC2,
    0x00, 0x62, 0x08, 0x9D, 0x7A, 0x10, 0x32, 0xB2, 0x7C, 0xBB, 0x5A, 0x51, 0xDB, 0xBF, 0xFB, 0xE1,
    0xAF, 0x20, 0x81, 0x11, 0xCE, 0xA1, 0x9F, 0x79, 0x36, 0x2C, 0x8A, 0xB3, 0x0C, 0xF0, 0xDF, 0x38,
    0x9C, 0xC9, 0x1D, 0x25, 0xEF, 0xDF, 0x4F, 0x6C, 0xC5, 0x3B, 0x74, 0x6F, 0x9D, 0xAD, 0x36, 0x05,
    0x67, 0xC5, 0x9A, 0xAE, 0x2D, 0x6D, 0xAA, 0xDB, 0x0E, 0xE0, 0xA6, 0x67, 0x45, 0xC5, 0x8E, 0xF8,
    0xC2, 0x1E, 0x64, 0xB8, 0x64, 0xA1, 0x46, 0x18, 0x2D, 0x4A, 0xBA, 0x67, 0x8A, 0x30, 0xB5, 0x75,
    0x05, 0x20, 0x96, 0x0D, 0xCE, 0xCC, 0xEC, 0x1F, 0x8C, 0xFD, 0xB0, 0x4F, 0xCD, 0x4C, 0x35, 0x46,
    0x35, 0x7D, 0xD1, 0xF5, 0x83, 0xC7, 0xA3, 0x2E, 0x2E, 0x33, 0x30, 0x1C, 0x92, 0xC9, 0x54, 0xAA,
    0xD2, 0x0F, 0x66, 0x51, 0x50, 0x85, 0x72, 0x6D, 0xBF, 0x8C, 0x57, 0x73, 0xA0, 0xD9, 0xBD, 0x72,
    0x75, 0x5A, 0xE1, 0x3B, 0x98, 0x06, 0x7D, 0x99, 0x4F, 0xBE, 0xA3, 0xB3, 0x63, 0xB2, 0xD0, 0x18,
    0x17, 0xE9, 0xC1, 0xEA, 0x3B, 0x90, 0x04, 0x57, 0xB4, 0x52, 0xDC, 0x28, 0x33, 0xE1, 0x71, 0x84,
    0x97, 0xA7, 0xE2, 0xAD, 0xBB, 0xBC, 0xAB, 0xBB, 0xF2, 0x17, 0x5D, 0xC8, 0xEE, 0xE5, 0x32, 0xF8,
    0x9C, 0x32, 0x27, 0x10, 0x5B, 0x20, 0x7B, 0x12, 0x46, 0x3D, 0xC2, 0x89, 0x15, 0x6F, 0xB9, 0x1B,
    0x32, 0xEE, 0x5D, 0x7A, 0x8E, 0x13, 0x33, 0x8B, 0xE5, 0x36, 0x60, 0xAA, 0x38, 0x53, 0x50, 0x74,
    0xB2, 0x86, 0x92, 0x5F, 0xA0, 0xFB, 0xC6, 0xEE, 0x0C, 0xCE, 0xDA, 0xA2, 0xFE, 0x5D, 0x35, 0xEA,
]

flag = ''
characters = '_abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ-.$@!?{} '
state = b'\x00' * 36
lib = ctypes.CDLL('./checker.so')

for i in tqdm(range(0x31)):
    for c in characters:
        state_array = ctypes.create_string_buffer(state)
        lib.update(state_array, ctypes.c_char(bytes(c, 'ascii')))
        res = bytes(state_array)

        if list(res[:16]) == table[16*i:16*i+16]:
            state = res
            flag += c

print(flag)