otp
- 生成プログラムotp.binを逆アセンブルしてみると、sha256関連の関数が含まれていた。
- よって、sha256がOTPの生成に大きな役割を果していると考えられるので、METASMを用いてフックして実行してみた。
1 require 'metasm'
2
3 process = Metasm::OS.current.create_process('./otp.bin -g token65.bin')
4 debugger = process.debugger
5 debugger.bpx(0x40613a, false) do
6 puts "SHA256_hash:"
7 addr = debugger[:rdi]
8 size = debugger[:rsi]
9 @addr2 = debugger[:rdx]
10 p ['%x'%addr,size]
11 p debugger[addr,size].unpack("H*")
12 end
13 debugger.bpx(0x4061ae, false) do
14 puts "result: %s" % debugger[@addr2,32].unpack("H*")
15 end
16 debugger.run_forever
- すると、OTPは次のような形式となっていることが分かった。
16文字: ユーザー名+secretの上位7byteをそれぞれ1byte毎にsha256sumしてxorしたもの 16文字: sequenceを16進数表記にしたもの 32文字: sequence + secretのsha256sumの先頭32文字
- 解析した生成部をRubyプログラムに直すと次のようになる。
1 require 'digest/sha2'
2 require 'facets'
3 def gen(user, secret, seq)
4 s = "\0" * (user.size + 8)
5 d = "\0" * 8
6 user.chars.each.with_index do |c,i|
7 s[i] = c
8 digest = Digest::SHA256.digest(s)
9 d ^= digest
10 s[i] = "\0"
11 end
12 8.times do |i|
13 next if i == 0
14 s[user.size + i] = if i == 0
15 0.chr
16 else
17 ((secret >> (i*8)) & 255).chr
18 end
19 digest = Digest::SHA256.digest(s)
20 d ^= digest
21 s[user.size + i] = "\0"
22 end
23 f_hash = d.unpack("H*")[0]
24
25 sequence = [seq].pack("q").unpack("H*")[0]
26
27 s_hash = Digest::SHA256.hexdigest([seq].pack("q") + [secret].pack("q"))
28
29 f_hash + sequence + s_hash[0,32]
30 end
31
32 p gen('nomeaning',2459565876494672537,37)
- adminのsecretを特定する必要がある。
- まず先頭の16文字のsignatureから、7byteを半分全列挙しつつ全探索することで特定できる。
- 次のようなプログラムで特定した。
1 require 'digest/sha2'
2 require 'facets'
3 target = ["9ae684ca583214d3"].pack("H*") # 目的とするHash
4 "admin".chars.each.with_index do |c,i| # ユーザー名によるHashを打ち消す
5 s = "\0" * 13
6 s[i] = c
7 target ^= Digest::SHA256.digest(s)
8 end
9
10 # 1792 vars equation
11 matrix = Array.new(128){Array.new(1793)}
12 (0...7).each do |pos|
13 (0...256).each do |char|
14 s = "\0" * 13
15 s[pos + 6] = char.chr
16 digest = Digest::SHA256.digest(s)[0,8]
17 # 256bit
18 print '%d' % digest.unpack("H*")[0].to_i(16)
19 print ' '
20 end
21 puts
22 end
23
24 puts target.unpack("H*")[0].to_i(16)
1 #include <bitset>
2 #include <algorithm>
3 #include <iostream>
4 #include <vector>
5 #include <cstdint>
6 using namespace std;
7 unsigned long long a[7][256], target;
8 unsigned long long list[256*256*256];
9 unsigned long long result;
10 int sel[7];
11 int from, to;
12 int search(int pos, unsigned long long cur) {
13 if(pos == 7) {
14 if(binary_search(list, list+256*256*256, cur)) {
15 cout << "-----" << endl;
16 cout << cur << endl;
17 for(int i = 0; i < 7; i++) {
18 cout << sel[i] << endl;
19 }
20 result = cur;
21 cout << "-----" << endl;
22 }
23 return 1;
24 }
25 for(int i = 0; i < 256; i++) {
26 if(pos == 3 && !(from <= i && i < to))continue;
27 sel[pos] = i;
28 search(pos + 1, cur ^ a[pos][i]);
29 if(pos == 3) cout << "search:" << i << endl;
30 }
31 return 1;
32 }
33
34 int main(int argc, char **argv) {
35 from = atoi(argv[1]);
36 to = atoi(argv[2]);
37 cerr << from << "," << to << endl;
38 for(int i = 0; i < 7; i++) {
39 for(int j = 0; j < 256; j++) {
40 cin >> a[i][j];
41 }
42 }
43 cin >> target;
44 // 3つを選択する
45 for(int i = 0; i < 256; i++) {
46 for(int j = 0; j < 256; j++) {
47 for(int k = 0; k < 256; k++) {
48 list[i*256*256+j*256+k] = a[0][i] ^ a[1][j] ^ a[2][k];
49 }
50 }
51 }
52 sort(list, list+256*256*256);
53 search(3, target);
54 for(int i = 0; i < 256; i++) {
55 for(int j = 0; j < 256; j++) {
56 for(int k = 0; k < 256; k++) {
57 if(result == (a[0][i] ^ a[1][j] ^ a[2][k])) {
58 cout << i << endl << j << endl << k << endl;
59 }
60 }
61 }
62 }
63 }
$ ruby solve.rb | ./search 0 256 ... ----- 9807512490306708300 0 0 0 140 153 187 73 ----- search:140 ... 193 14 67
- 残りの1byteは後ろのハッシュとマッチするものを探索すれば良い。次のプログラムでsecretを求めてさらに次のOTPを表示した。
$ ruby gen.rb 9ae684ca583214d33a0500000000000030949b105cb796db1a0488099b684373
このOTPでログインしたところ、フラグ31C3_a7e3683344e954efd8a58a2a3da7fbe8が得られた。