ふるつき

私は素直に思ったことを書いてるけど、上から目線だって言われる

angrの練習をする

pip install angr しておく

#include <stdio.h>

int main() {
    char str[32];
    printf("HELLO\n>");
    scanf("%31s", str);

    int a = *(int *)str;
    int b = *(int *)(str + 4);

    if (a != 0x41414141) {
        printf("BAD...\n");
    }
    else if (a + 0x01010202 == b) {
        printf("CORRECT!\n");
    }
    else {
        printf("BAD...\n");
    }

}

これの CORRECT! を出させたい。

gcc hoge.c -o hoge -fno-pie -no-pie

objdumpなりで CORRECT が呼ばれるようなアドレスを探る。私の環境では 0x400614 だった。

import angr

p = angr.Project("hoge", load_options={'auto_load_libs': False})

r = p.surveyors.Explorer(find=0x400614).run()

import code
code.interact(local=locals())

このコードは思考停止でこういうものだって憶えるほうが楽そうだったのでそうした。

python solve.py とするとすぐにインタラクティブシェルが起動する。

>>> r.found[0].state.posix.dumps(0)
'AAAACCBB\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd9\xd1\xd9\xd9\xd9\xd9\xd9\xd9\xd9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

この r...は本当に全然理解してないのでこういうものだということにしておく。

# ./hoge 
HELLO
>AAAACCBB
CORRECT!

#include <stdio.h>
#include <string.h>

int chk1(char*s) {
    if (strlen(s) != 8) {
        return 0;
    }

    int a =  *(int*)s;
    if (a != 0x41414142) {
        return 0;
    }

    return 1;
}

int chk2(char*name, char*pass) {
    int a = *(int*)pass;
    int b = *(int*)(pass+4);
    if (a+b == *(int*)(name+4)) {
        return 1;
    }

    return 0;
}

int main() {
    char str1[32];
    char str2[32];

    printf("HELLO\n>");
    scanf("%31s", str1);
    printf("\n>");
    scanf("%31s", str2);

    if (! chk1(str1)) {
        printf("BAD...\n");
    }
    else if (chk2(str1, str2)) {
        printf("CORRECT!\n");
    }
    else {
        printf("BAD...\n");
    }
}

ちょっとむずかしそうにした。おんなじようにコンパイルする。

今回はfindじゃなくてavoidを指定してみる。どっちも指定することももちろんできる。

import angr

p = angr.Project("hoge", load_options={'auto_load_libs': False})

r = p.surveyors.Explorer(avoid=(0x4006e0, 0x40070f)).run()
import code
code.interact(local=locals())

実行すると、きっとまた1path foundedかと思ったらdeadendが見つかった。

>>> r.deadended[0].state.posix.dumps(0)
'BAAAD\xc7\x92O\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00gU\x19J\xddqy\x05\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
>>> r.deadended[0].state.posix.dumps(0).replace("\x01", "").split("\x00")
['BAAAD\xc7\x92O', '', 'gU\x19J\xddqy\x05', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '']

なんかそれっぽい値が残った。標準入力に与えられないので、

# printf 'BAAAD\xc7\x92O\ngU\x19J\xddqy\x05' | ./hoge
HELLO
>
>CORRECT!

いろいろ試行錯誤しながらやってると本質以外のところがコードを読みにくくしてくけど気にしないことにした。こんどは入力の一部がファイルから与えられて単純にangrできない。

#include <stdio.h>
#include <string.h>


int chk1(char*s) {
    if (s[0] != 't') {
        return 0;
    }
    int i;
    int xs[] = {-5, -28, 0, 28, 6};
    for (i = 0; i < 5; i++) {
        if (s[i]+s[i+1]-s[i+2]-s[i+3] != xs[i]) {
            return 0;
        }
    }

    return 1;
}

int chk2(char*u, char*p) {
    int a = *(int*)u;
    int b = *(int*)p;
    if ((a^b) != 806226176) {
        return 0;
    }

    a = *(int*)(u+4);
    b = *(int*)(p+4);
    if ((a^b) != 235539743) {
        return 0;
    }

    return 1;
}

int main() {
    char str1[32];
    char str2[32];

    FILE *fp = fopen("username.txt", "r");
    fscanf(fp, "%31s", str1);
    printf("PASSWORD\n>");
    scanf("%31s", str2);
    
    if (!chk1(str1)) {
        printf("BAD...\n");
    }
    else if (chk2(str1, str2)) {
        printf("CORRECT!\n");
    }
    else {
        printf("BAD...\n");
    }
}

chk1のところだけ切り出してみたところ takoyaki がstr1の要件を満たすところまでわかったとする(ほかにもstr1となる文字列はあるけど)。というわけでchk2を通過できるようなstr2だけを求めれば良い。というわけでchk1の呼び出しを飛ばして、chk2の直前から追いかけてみる。

gdbでchk2が呼ばれる直前までを実行してレジスタやスタックにどのような値が積まれているか確認する。

みたところ、スタックに入力が積まれ、rdi、rsiにstr1、str2のアドレスが入っている。angrでもそんな状態を作ってやる。

import angr

p = angr.Project("hoge", load_options={'auto_load_libs': False})

s = p.factory.blank_state(addr=0x4007f4)

str1 = s.se.BVV("takoyaki")
str2 = s.se.BVS("hogehoge", 32*8)

str1p = 0x7fffffffdfd0
str2p = 0x7fffffffd000

s.memory.store(str1p, str1)
s.memory.store(str2p, str2)

s.regs.rdi = str1p
s.regs.rsi = str2p

pg = p.factory.path_group(s, immutable=False)
pg.explore(find=0x00000000004007fd)

import code
code.interact(local=locals())

大幅に変わってしまった。まず、blank_state(addr)で、開始時のeipを指定してやる。その戻り値でいろいろやったものからほげるとできる。

BVVは固定値で、BVSはangrが変更することがある値(というか求めたい値)。これらをmemory.storeで適当な位置に埋め込んでやる。一応gdbの値をみて適当なところにおいたけどそんなにこだわらなくても良さそう。

アドレスをrdiとかrsiにも入れて、引数にする。

実行はそんなに重くない。

>>> pg.found[0].state.se.any_str(str2)
'the_flag\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'