yyy

CTFつよくなりたい

CSAW CTF Qualification Round 2017 Writeup

開催期間(JST)

09/16 AM5:00 ~ 09/18 AM5:00

結果

・チーム名:wabisabi

・得点:851 pt

・順位:得点したチーム中,231/1444

解いた問題

・CVV (Misc100)

・tablEZ (Reversing100)

・Best Router (Forensics200)

・realism (Reversing400)

取り組んだが解けなかった問題

・pilot (Pwn75)

・Missed Registration (Forensics150)

・Gopherz (Reversing350)

はじめに

今回はEKOPARTYのほうと被っていて,一応両方登録はしたんですが,結局CSAWだけやってました.

土曜日は@kobadlveと@phustlyと集まって少しやっていて,日曜は家に引きこもってやってました.

去年のCSAWは576ptで252/1274だったので,一応去年より高い順位は取れたっぽい.

Writeup

CVV (Misc100)

ncで繋ぐと,こんな感じでいろいろな種類のクレジットカード番号を求められます.

$ nc misc.chal.csaw.io 8308
I need a new American Express!
^C

$ nc misc.chal.csaw.io 8308
I need a new Visa!

クレジットカードのパターンは,'MasterCard','Discover','American','Visa'の4つ.

確か「面白くて眠れなくなる数学」とかいう本を以前読んだ時に,クレジットカード番号はとあるアルゴリズムによって決まっているってことを知って,つまりランダムに数字を入力すればいいというわけではなくて,そのアルゴリズムに沿った数字を入力する必要がありそうだなぁとか思いました.

各クレジットカードについてもカード番号の桁数が違っていたり,それぞれカード番号にルールが決まっていたりするので,そこら辺を気を付けながらスクリプトを書きます.

使われているアルゴリズムの名前はLuhnアルゴリズムというものらしく,どうせ実装してる人いるやろって思ったらWikipediaに載ってたり,いろいろな書き方で実装している人がいたので,それをお借りしました.

Luhnアルゴリズム(やってみた Python2.7) · GitHub

雑にスクリプトを書いて回してみたら,Visaのカード番号を毎回失敗するVisaが苦手なスクリプトが生まれてしまって,どうしようかなと思った結果,以下の任意のクレジットカード番号を生成する神サイトを見つけたので,適当に100個くらい生成して,それを使うようにしました.

www.getcreditcardnumbers.com

あとは,何回か正解すると途中から出題が変わって,ある数字で始まるもの,終わるもの,与えられた番号がvalidかinvalidか返すもの,とかにそれぞれスクリプトを雑に対応させました.

与えられた番号がvalidかinvalidか返すやつは,Luhnアルゴリズム自体が全ての間違いを検出できるわけではないっぽくて,2回目で成功しました.

以下,書いたスクリプト.(めっちゃ汚いけど,解ければいいやろと思ってる)

Luhnアルゴリズムでvalidが返るまでランダムに数字を生成して,それを送るってことをやってる.1度使ったものは使えないっぽいけど,これだけ大きければほぼ被ることはないだろうということで,その対策はしてない.

#!/usr/bin/env python
# coding: utf-8
import socket
import random
import itertools

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('misc.chal.csaw.io', 8308))

card_list = ['MasterCard', 'Discover', 'American', 'Visa']

#  https://gist.github.com/oyakata/5955744 より
def check_digits(text):
    """itertools.cycleを使って実装。"""
    def even_value(x):
        y = x * 2
        return y - 9 if y > 9 else y
    values = (int(x) for x in reversed(text))
    switcher = itertools.cycle((lambda x: x, even_value))
    digits = (func(x) for func, x in itertools.izip(switcher, values))
    return sum(digits) % 10 == 0


def main():
    count = 0
    f = open('visa.txt', 'r')
    visa = f.read().split('\n')
    f.close()

    while(True):
        data = s.recv(256)
        print data

        all_data  = data.split(' ')

        card = all_data[4].replace('!','').rstrip()

        if card == 'card':
            card = all_data[8].replace('!', '').rstrip()

        print card

        if card == 'MasterCard':
            while True:
                num = '5' + str(random.randint(100000000000000, 999999999999999))
                print num
                if check_digits(num):
                    break

        elif card == 'Discover':
            while True:
                num = '60110' + str(random.randint(10000000000, 99999999999))
                print num
                if check_digits(num):
                    break

        elif card == 'American':
            while True:
                num = '34' + str(random.randint(1000000000000, 9999999999999))
                print num
                if check_digits(num):
                    break

        elif card == 'Visa':
            print visa[count]
            num = visa[count]

        elif card == 'if':
            card_num = all_data[5]
            if check_digits(card_num):
                num = '1'
            else:
                num = '0'

        else:
            info = all_data[6]

            if len(card) == 4 and info == "starts":
                while True:
                    num = card + str(random.randint(100000000000, 999999999909))
                    print num
                    if check_digits(num):
                        break
            elif len(card) == 1 and info == "ends":
                while True:
                    num = str(random.randint(100000000000000, 999999999999999)) + card
                    print num
                    if check_digits(num):
                        break
            elif len(card) == 4 and info == "ends":
                while True:
                    num = str(random.randint(100000000000, 999999999909)) + card
                    print num
                    if check_digits(num):
                        break

        s.sendall(num + '\n')
        count = count + 1


if __name__ == '__main__':
    main()
$ python solve.py
.
.
.

if
True
Thanks!
I need to know if 7358400188115005 is valid! (0 = No, 1 = Yes)

if
True
Thanks!
I need to know if 8664780687553453 is valid! (0 = No, 1 = Yes)

if
False
Thanks!
I need to know if 1514774928101638 is valid! (0 = No, 1 = Yes)

if
False
Thanks!
I need to know if 9017155594857850 is valid! (0 = No, 1 = Yes)

if
True
Thanks!
I need to know if 3591469017167338 is valid! (0 = No, 1 = Yes)

if
False
Thanks!
flag{ch3ck-exp3rian-dat3-b3for3-us3}

tablEZ (Reversing100)

一番最初に解いたやつ.

問題文

Bobby was talking about tables a bunch, so I made some table stuff. I think this is what he was talking about...
$ ./true
Please enter the flag:
hoge
WRONG

crackme系のやつ.

確か,入力値を元にテーブルからindexを求めて,それを使って再度テーブルから値を取り出す,ってことをやっていた気がします.

以下,解いていた時のメモ.

gdb-peda$ x/80 0x555555755281
0x555555755281 <trans_tbl+1>: 0x056c04c4039b02bb  0x09450822072e064a
0x555555755291 <trans_tbl+17>:    0x0d060cd50bb80a33  0x117910fa0fbc0e0a
0x5555557552a1 <trans_tbl+33>:    0x15bf14b213e11224  0x1960188617ad162c
0x5555557552b1 <trans_tbl+49>:    0x1d591cd81bb61aa4  0x217720941f411e87
0x5555557552c1 <trans_tbl+65>:    0x256124cb234f22f0  0x292a289727c02625
0x5555557552d1 <trans_tbl+81>:    0x2d9f2cc92b082a5c  0x31f930cf2f4e2e43
0x5555557552e1 <trans_tbl+97>:    0x35e73465336f323e  0x39ef38b7373936c5
0x5555557552f1 <trans_tbl+113>:   0x3daa3c2f3bc83ad0  0x4181403c3f473ec7
0x555555755301 <trans_tbl+129>:   0x45a644d343494232  0x49404858472b4696
0x555555755311 <trans_tbl+145>:   0x4d1a4cee4b9c4af1  0x518050d64fc64e5b
0x555555755321 <trans_tbl+161>:   0x553d549a536d522d  0x59e05884579356a7
0x555555755331 <trans_tbl+177>:   0x5d095cb95b3b5a12  0x614860995fba5e69
0x555555755341 <trans_tbl+193>:   0x6582647c63b16273  0x69fb689d672766be
0x555555755351 <trans_tbl+209>:   0x6db36cf46b7e6a67  0x711b705f6fc26e05
0x555555755361 <trans_tbl+225>:   0x7511747173237254  0x796878a577d27630
0x555555755371 <trans_tbl+241>:   0x7d7a7cf57b3f7a9e  0x8185800c7f0b7ece
0x555555755381 <trans_tbl+257>:   0x858e845e836382de  0x89da886a87fe86bd
0x555555755391 <trans_tbl+273>:   0x8dac8ce88b888a26  0x91f690a88f628e03
0x5555557553a1 <trans_tbl+289>:   0x95c3946b937592f7  0x998f98e697519646
0x5555557553b1 <trans_tbl+305>:   0x9d919c5a9b769a28  0xa152a0449f1f9eec
0x5555557553c1 <trans_tbl+321>:   0xa53aa48ba3fca201  0xa910a816a7a3a6a1
0x5555557553d1 <trans_tbl+337>:   0xad95accaab50aa14  0xb10eb035af4bae92
0x5555557553e1 <trans_tbl+353>:   0xb55db41db320b2b5  0xb90fb86eb7e2b6c1
0x5555557553f1 <trans_tbl+369>:   0xbdd9bcd4bb90baed  0xc157c098bfddbe42
0x555555755401 <trans_tbl+385>:   0xc556c478c319c237  0xc904c8d1c774c6af
0x555555755411 <trans_tbl+401>:   0xcd4ccce5cb55ca29  0xd1dbd089cff2cea0
0x555555755421 <trans_tbl+417>:   0xd5ead483d338d2e4  0xd98cd8dcd707d617
0x555555755431 <trans_tbl+433>:   0xdde9dc7bdbb4da8a  0xe10de015dfebdeff
0x555555755441 <trans_tbl+449>:   0xe534e4f3e3a2e202  0xe913e8f8e718e6cc
0x555555755451 <trans_tbl+465>:   0xed21ecaeeb7fea8d  0xf170f04defcdeee3
0x555555755461 <trans_tbl+481>:   0xf572f4abf3fdf253  0xf9a9f866f71cf664
0x555555755471 <trans_tbl+497>:   0xfddffcd7fb1efab0  0xe0000031ff7dfe36


0x5555555549c1 <main+289>:    cmp    QWORD PTR [rbp-0xc8],0x25
FLAGは0x25文字(37文字)


This is the flag.
gdb-peda$ x/30 0x7fffffffcac0
0x7fffffffcac0: 0xb1e711 f59d73b327  0x30f4f9f9b399beb3
0x7fffffffcad0: 0xb19965237399711b  0xf9279923be111165
0x7fffffffcae0: 0x000000 ce65059923

逆の処理を書くより,入力値を与えてテーブル作って,そこからflagを求めたほうが早そうなので,その方法でやりました.

最近バイトでRubyばっかりなのでRubyで書いてみた.

string = "d0efb739c5e7656f3ef999cef53b12e08493a73d9a6d2d80d6c65b1aee9cf140582b96a6d349323f9e68a5d230117123541b5fc205b3f47e67fb9d27be827cb173"
char = (('a'..'z').to_a.join + ('A'..'Z').to_a.join + '{}_' + ('0'..'9').to_a.join)

list1 = string.scan(/.{1,2}/).reverse
list2 = char.scan(/.{1,1}/)
table = {}

list1.zip(list2).each do |val1, val2|
  table.store(val1, val2)
end

flag_str  = "ce65059923f9279923be111165b19965237399711b30f4f9f9b399beb3b1e711f59d73b327"
flag_list = flag_str.scan(/.{1,2}/).reverse
flag = ""

flag_list.each do |val|
  flag = flag + table[val].to_s
end

puts flag
$ ruby solve.rb
flag{t4ble_l00kups_ar3_b3tter_f0r_m3}

Best Router (Forensics200)

問題文

http://forensics.chal.csaw.io:3287

NOTE: This will expand to ~16GB!

解いているチームが多かったのでできそうだなぁとは思ってたのですが,16GBも容量がなかったので,土曜家に帰ってきてからDesktopのほうで解いたやつ.

与えられたbest_router.tar.gzを解凍すると,14.5GBのイメージファイルが出てきます.

FTK Imagerで解けそうなので,開いてみる.

f:id:ywkw1717:20170918145543p:plain

いろいろありそうだけど,問題文を思い出してみる.

ログインフォームのURLが与えられているので,これにログインできるとflagが出そう.

f:id:ywkw1717:20170918150745p:plain

/var/wwwが怪しそうなので見てみるとビンゴ.

f:id:ywkw1717:20170918150209p:plain

flag.txtは空でここからは見れないので,やっぱり与えられたURLにログインする必要がありそう.

f:id:ywkw1717:20170918150326p:plain

username:admin

psassword:iforgotaboutthemathtest

でログインするとflagが貰える.

f:id:ywkw1717:20170918150838p:plain

realism (Reversing400)

高得点の解きたいなぁと思って,rev問見てたら数十人が解いていたのでやってみた.

問題文

Did you know that x86 is really old? I found a really old Master Boot Record that I thought was quite interesting! At least, I think it's really old...

qemu-system-i386 -drive format=raw,file=main.bin

QEMU初めて使った.

与えられたmain.binはブートセクタ.

与えれらたコマンド通りにmain.binを起動してみると,以下のような画面が出てきて,またこれもcrackme系の問題.

f:id:ywkw1717:20170918151711p:plain

適当に入力すると,WRONG FLAG.

f:id:ywkw1717:20170918152515p:plain

ブートセクタは512バイトで読むべきコード自体は少ないのですが,どう解析したらいいかわからなくて困った.

ググってたらndisasmでできるよ,みたいなのを見つけて逆アセンブルしたらそれっぽく出てきたけど,やっぱり動的解析しないと厳しそう.

めっちゃ調べていろいろやってみたら,以下のようにして解析できた.

gdbオプション付きでQEMUを起動.

$ qemu-system-i386 -drive format=raw,file=main.bin -S -gdb tcp::1234

別terminalにてgdbを起動して,set architecturetarget remoteでそれぞれ指定.

ブートセクタは0x7C00にロードされるらしいので,そこにブレークポイントをしかける.

$ gdb
gdb-peda$ set architecture i8086
gdb-peda$ target remote localhost:1234
gdb-peda$ b *0x7c00

pedaのプロンプトは出ているのに使えなかったので,ノーマルなgdbでノーマルなコマンドで解析する.

最初の4文字が"flag"かどうかチェックしている部分.入力値の長さのチェックはこれの前にある.

0x7c6f:  cmp    DWORD PTR ds:0x1234,0x67616c66
0x7c78:    jne    0x7d4d

その後,入力値チェックがある.あとはここを読んで求解処理を書くだけ.

0x7c7c:  movaps xmm0,XMMWORD PTR ds:0x1238
0x7c81:    movaps xmm5,XMMWORD PTR ds:0x7c00
0x7c86:    pshufd xmm0,xmm0,0x1e
0x7c8b:    mov    si,0x8
0x7c8e:    movaps xmm2,xmm0
0x7c91:    andps  xmm2,XMMWORD PTR [si+0x7d90]
0x7c96:    psadbw xmm5,xmm2
0x7c9a:    movaps XMMWORD PTR ds:0x1268,xmm5
0x7c9f:    mov    di,WORD PTR ds:0x1268
0x7ca3:    shl    edi,0x10
0x7ca7:    mov    di,WORD PTR ds:0x1270
0x7cab:    mov    dx,si
0x7cad:    dec    dx
0x7cae:    add    dx,dx
0x7cb0:    add    dx,dx
0x7cb2:    cmp    edi,DWORD PTR [edx+0x7da8]
0x7cba:    jne    0x7d4d
0x7cbe:    dec    si
0x7cbf:    test   si,si # If si is 0, finish.
0x7cc1:    jne    0x7c8e

8回ループして入力値チェックしている.間違っていたら即Wrong.

最初にxmm0レジスタに読み込んだ入力値をpshufd命令でシャッフルしている.

0x7c86: pshufd xmm0,xmm0,0x1e

このpshufd命令までが初期化っぽい.

その後,andps命令でシャッフルされた入力値をマスクする.(16バイトの内,ある2バイトを0にする.この0にする位置は徐々にずれていく)

0x7c91: andps  xmm2,XMMWORD PTR [si+0x7d90]

そして,マスクした入力値とxmm5レジスタを使ってpsadbw命令で差の絶対値を取ったり足したりする.(このxmm5レジスタの値は初期値だけ決まっていて,あとはpsadbwで求まった値を次の計算でも使う)

0x7c96: psadbw xmm5,xmm2

psadbw命令は以下がわかりやすい.

http://www.officedaytime.com/tips/simdimg/si.php?f=psadbw

その後,求まった値を使って8バイトの数値を作って,それが特定の値と等しいかどうかチェックしている.

1回目と2回目で各命令を実行したときのレジスタの値とかを逐一調べてメモしてた.

2回目の値も調べるために,バイナリ本体にパッチを当てちゃうと便利.

この部分を

0x7cba: jne    0x7d4d

以下のように書き換えて,オペコードをjeにする.

f:id:ywkw1717:20170918162540j:plain

解いていた時のメモ.

Initialize
=======================================================================================
=> 0x7c6f:   cmp    DWORD PTR ds:0x1234,0x67616c66
   0x7c78:  jne    0x7d4d
   0x7c7c:  movaps xmm0,XMMWORD PTR ds:0x1238

gdb-peda$ p $xmm0
$2 = {
  v4_float = {12.0784864, 12.0784311, 12.0784311, 1.60549888e+37}, 
  v2_double = {2261634.5098039485, 2.2040338477057924e+295}, 
  v16_int8 = {0x7b, 0x41 <repeats 14 times>, 0x7d}, 
  v8_int16 = {0x417b, 0x4141, 0x4141, 0x4141, 0x4141, 0x4141, 0x4141, 0x7d41}, 
  v4_int32 = {0x4141417b, 0x41414141, 0x41414141, 0x7d414141}, 
  v2_int64 = {0x414141414141417b, 0x7d41414141414141}, 
  uint128 = 0x7d41414141414141414141414141417b
}

   0x7c81:  movaps xmm5,XMMWORD PTR ds:0x7c00

gdb-peda$ p $xmm5
$3 = {
  v4_float = {-134298496, -2.50091934, -1.48039995e-36, 1.93815862e-18}, 
  v2_double = {-8.0294250547975565, 1.241726856953559e-144}, 
  v16_int8 = {0xb8, 0x13, 0x0, 0xcd, 0x10, 0xf, 0x20, 0xc0, 0x83, 0xe0, 0xfb, 0x83, 0xc8, 0x2, 0xf, 0x22}, 
  v8_int16 = {0x13b8, 0xcd00, 0xf10, 0xc020, 0xe083, 0x83fb, 0x2c8, 0x220f}, 
  v4_int32 = {0xcd0013b8, 0xc0200f10, 0x83fbe083, 0x220f02c8}, 
  v2_int64 = {0xc0200f10cd0013b8, 0x220f02c883fbe083}, 
  uint128 = 0x220f02c883fbe083c0200f10cd0013b8
}

   0x7c86:  pshufd xmm0,xmm0,0x1e

gdb-peda$ p $xmm0
$4 = {
  v4_float = {12.0784311, 1.60549888e+37, 12.0784311, 12.0784864}, 
  v2_double = {2.2040338477057924e+295, 2261750.5098039214}, 
  v16_int8 = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x41, 0x41, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, 
  v8_int16 = {0x4141, 0x4141, 0x4141, 0x7d41, 0x4141, 0x4141, 0x417b, 0x4141}, 
  v4_int32 = {0x41414141, 0x7d414141, 0x41414141, 0x4141417b}, 
  v2_int64 = {0x7d41414141414141, 0x4141417b41414141}, 
  uint128 = 0x4141417b414141417d41414141414141
}

   0x7c8b:  mov    si,0x8
=======================================================================================


Loop
=======================================================================================
   0x7c8e:  movaps xmm2,xmm0

gdb-peda$ p $xmm2
$6 = {
  v4_float = {12.0784311, 1.60549888e+37, 12.0784311, 12.0784864}, 
  v2_double = {2.2040338477057924e+295, 2261750.5098039214}, 
  v16_int8 = {0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x41, 0x41, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, 
  v8_int16 = {0x4141, 0x4141, 0x4141, 0x7d41, 0x4141, 0x4141, 0x417b, 0x4141}, 
  v4_int32 = {0x41414141, 0x7d414141, 0x41414141, 0x4141417b}, 
  v2_int64 = {0x7d41414141414141, 0x4141417b41414141}, 
  uint128 = 0x4141417b414141417d41414141414141
}

   0x7c91:  andps  xmm2,XMMWORD PTR [si+0x7d90] # first, si is 0x8. second, si is 0x7.

0 の位置が徐々にずれていく.

First: gdb-peda$ x/30wx $si+0x7d90
0x7d98: 0xffffff00  0xffffffff  0xffffff00  0xffffffff
0x7da8: 0x02110270  0x02290255  0x025e0291  0x01f90233
0x7db8: 0x027b0278  0x02090221  0x0290025d  0x02df028f
0x7dc8: 0x00000014  0x00000000  0x00000000  0x00000000
0x7dd8: 0x00000000  0x00000000  0x00000000  0x00000000
0x7de8: 0x00000000  0x00000000  0x00000000  0x00000000
0x7df8: 0x00000000  0xaa550000  0x00000000  0x00000000
0x7e08: 0x00000000  0x00000000

Second: gdb-peda$ x/20wx $si+0x7d90
0x7d97: 0xffff00ff  0xffffffff  0xffff00ff  0xffffffff
0x7da7: 0x110270ff  0x29025502  0x5e029102  0xf9023302
0x7db7: 0x7b027801  0x09022102  0x90025d02  0xdf028f02
0x7dc7: 0x00001402  0x00000000  0x00000000  0x00000000
0x7dd7: 0x00000000  0x00000000  0x00000000  0x00000000


First: gdb-peda$ p $xmm2
$7 = {
  v4_float = {12.0783691, 1.60549888e+37, 12.0783691, 12.0784864}, 
  v2_double = {2.2040338477057629e+295, 2261750.5098038912}, 
  v16_int8 = {0x0, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x0, 0x41, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, 
  v8_int16 = {0x4100, 0x4141, 0x4141, 0x7d41, 0x4100, 0x4141, 0x417b, 0x4141}, 
  v4_int32 = {0x41414100, 0x7d414141, 0x41414100, 0x4141417b}, 
  v2_int64 = {0x7d41414141414100, 0x4141417b41414100}, 
  uint128 = 0x4141417b414141007d41414141414100
}

Second: gdb-peda$ p $xmm2
$6 = {
  v4_float = {12.062562, 1.60549888e+37, 12.062562, 12.0784864}, 
  v2_double = {2.2040338476982412e+295, 2261750.5097961728}, 
  v16_int8 = {0x41, 0x0, 0x41, 0x41, 0x41, 0x41, 0x41, 0x7d, 0x41, 0x0, 0x41, 0x41, 0x7b, 0x41, 0x41, 0x41}, 
  v8_int16 = {0x41, 0x4141, 0x4141, 0x7d41, 0x41, 0x4141, 0x417b, 0x4141}, 
  v4_int32 = {0x41410041, 0x7d414141, 0x41410041, 0x4141417b}, 
  v2_int64 = {0x7d41414141410041, 0x4141417b41410041}, 
  uint128 = 0x4141417b414100417d41414141410041
}

   0x7c96:  psadbw xmm5,xmm2

First: gdb-peda$ p $xmm5
$9 = {
  v4_float = {8.88423226e-43, 0, 1.06919073e-42, 0}, 
  v2_double = {3.1323761946335031e-321, 3.7697208777687111e-321}, 
  v16_int8 = {0x7a, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xfb, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  v8_int16 = {0x27a, 0x0, 0x0, 0x0, 0x2fb, 0x0, 0x0, 0x0}, 
  v4_int32 = {0x27a, 0x0, 0x2fb, 0x0}, 
  v2_int64 = {0x27a, 0x2fb}, 
  uint128 = 0x00000000000002fb000000000000027a
}
====================
>>> hex(0x22 - 0x41)
'-0x1f'
>>> hex(0x0f - 0x41)
'-0x32'
>>> hex(0x02 - 0x41)
'-0x3f'
>>> hex(0xc8 - 0x7b)
'0x4d'
>>> hex(0x83 - 0x41)
'0x42'
>>> hex(0xfb - 0x41)
'0xba'
>>> hex(0xe0 - 0x41)
'0x9f'
>>> hex(0x83 - 0x00)
'0x83'
>>> hex(0x1f + 0x32 + 0x3f + 0x4d + 0x42 + 0xba + 0x9f + 0x83)
'0x2fb'
====================

Second: gdb-peda$ p $xmm5
$7 = {
  v4_float = {7.13260918e-43, 0, 8.91225823e-43, 0}, 
  v2_double = {2.5147941373319449e-321, 3.142257507550328e-321}, 
  v16_int8 = {0xfd, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7c, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}, 
  v8_int16 = {0x1fd, 0x0, 0x0, 0x0, 0x27c, 0x0, 0x0, 0x0}, 
  v4_int32 = {0x1fd, 0x0, 0x27c, 0x0}, 
  v2_int64 = {0x1fd, 0x27c}, 
  uint128 = 0x000000000000027c00000000000001fd
}

   0x7c9a:  movaps XMMWORD PTR ds:0x1268,xmm5
   0x7c9f:  mov    di,WORD PTR ds:0x1268

First: gdb-peda$ p $edi
$10 = 0x27a

Second: gdb-peda$ p $di
$9 = 0x1fd

   0x7ca3:  shl    edi,0x10

First: gdb-peda$ p $edi
$11 = 0x27a0000

Second: gdb-peda$ p $edi
$11 = 0x1fd0000

   0x7ca7:  mov    di,WORD PTR ds:0x1270

First: gdb-peda$ p $edi
$12 = 0x27a02fb

gdb-peda$ p $si
$13 = 0x8

Second: gdb-peda$ p $edi
$12 = 0x1fd027c

gdb-peda$ p $si
$13 = 0x7

   0x7cab:  mov    dx,si
   0x7cad:  dec    dx

gdb-peda$ p $dx
$14 = 0x7

------------------------------ # 単純にdxの値を4倍している
   0x7cae:  add    dx,dx

First: gdb-peda$ p $dx # 7 + 7
$15 = 0xe

Second: gdb-peda$ p $dx # 6 + 6
$15 = 0xc

   0x7cb0:  add    dx,dx

First: gdb-peda$ p $dx
$16 = 0x1c

Second: gdb-peda$ p $dx
$16 = 0x18
------------------------------

   0x7cb2:  cmp    edi,DWORD PTR [edx+0x7da8]

First: gdb-peda$ x/10 $edx+0x7da8
0x7dc4: 0x02df028f  0x00000014  0x00000000  0x00000000
0x7dd4: 0x00000000  0x00000000  0x00000000  0x00000000
0x7de4: 0x00000000  0x00000000

Second: gdb-peda$ x/10 $edx+0x7da8
0x7dc0: 0x0290025d  0x02df028f  0x00000014  0x00000000
0x7dd0: 0x00000000  0x00000000  0x00000000  0x00000000
0x7de0: 0x00000000  0x00000000

   0x7cba:  jne    0x7d4d # If input is not correct, return.

   0x7cbe:  dec    si

gdb-peda$ p $si
$2 = 0x7

   0x7cbf:  test   si,si # If si is 0, finish.
   0x7cc1:  jne    0x7c8e
=======================================================================================


This is the flag!!!!!!!!!!!!!!!!!!!!!
Just do it!!!!!!!!!!

0x02df028f
0x0290025d
0x02090221
0x027b0278
0x01f90233
0x025e0291
0x02290255
0x02110270

psadbwのところが不可逆っぽいので,逆算する処理ではなくz3を使ってこの処理を満たすような入力値を求める.

z3の使い方がわからず,BitVecsで値を生成してそれにabs()使おうとしたらできなくて,他にもいろいろ苦戦して,ようやくスクリプトが書けました.

(かなり汚いです)

#!/usr/bin/env python
from z3 import *

# 0x20 <= si <= 7e
#
# [flag]
# 0x02df028f
# 0x0290025d
# 0x02090221
# 0x027b0278
# 0x01f90233
# 0x025e0291
# 0x02290255
# 0x02110270
#
# {s0s1s2s3s4s5s6s7s8s9s10s11s12s13}
#
# s7s8s9s10s11s12s13}s3s4s5s6{s0s1s2
#                  s15      s14
# 0x** ** ** 7b ** ** ** ** ** 7d ** ** ** ** ** ** ** (little endian)

def get_abs(x):
    return If(x >= 0,x,-x)

def main():
    solver = Solver()

    s = [Int('s_%d' % i) for i in range(14)]

    for i in range(len(s)):
        solver.add(s[i] != 0)
        solver.add(0x20 <= s[i], s[i] <= 0x7e)

    s14 = 0x7b
    s15 = 0x7d

    t0  = 0x22
    t1  = 0x0f
    t2  = 0x02
    t3  = 0xc8
    t4  = 0x83
    t5  = 0xfb
    t6  = 0xe0
    t7  = 0x83
    t8  = 0xc0
    t9  = 0x20
    t10 = 0x0f
    t11 = 0x10
    t12 = 0xcd
    t13 = 0x00
    t14 = 0x13
    t15 = 0xb8

    psadbw1 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - 0x00)
    psadbw2 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - 0x00)

    solver.add(psadbw1 == 0x28f)
    solver.add(psadbw2 == 0x2df)

    t0  = 0x0
    t1  = 0x0
    t2  = 0x0
    t3  = 0x0
    t4  = 0x0
    t5  = 0x0

    t6  = 0x02
    t7  = 0x8f

    t8  = 0x0
    t9  = 0x0
    t10 = 0x0
    t11 = 0x0
    t12 = 0x0
    t13 = 0x0

    t14 = 0x02
    t15 = 0xdf

    psadbw3 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - 0x00) + get_abs(t7 - s[3])
    psadbw4 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - 0x00) + get_abs(t15 - s[7])

    solver.add(psadbw3 == 0x25d)
    solver.add(psadbw4 == 0x290)

    t7  = 0x5d
    t15 = 0x90

    psadbw6 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - 0x00) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw7 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - 0x00) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw6 == 0x221)
    solver.add(psadbw7 == 0x209)

    t7  = 0x21
    t15 = 0x09

    psadbw8 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - 0x00) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw9 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - 0x00) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw8 == 0x278)
    solver.add(psadbw9 == 0x27b)

    t7  = 0x78
    t15 = 0x7b

    psadbw10 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - 0x00) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw11 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - 0x00) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw10 == 0x233)
    solver.add(psadbw11 == 0x1f9)

    t7  = 0x33
    t14 = 0x01
    t15 = 0xf9

    psadbw12 = get_abs(t0 - s[2]) + get_abs(t1 - s[1]) + get_abs(t2 - 0x00) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw13 = get_abs(t8 - s15) + get_abs(t9 - s[13]) + get_abs(t10 - 0x00) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw12 == 0x291)
    solver.add(psadbw13 == 0x25e)

    t7  = 0x91
    t14 = 0x02
    t15 = 0x5e

    psadbw14 = get_abs(t0 - s[2]) + get_abs(t1 - 0x00) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw15 = get_abs(t8 - s15) + get_abs(t9 - 0x00) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw14 == 0x255)
    solver.add(psadbw15 == 0x229)

    t7  = 0x55
    t15 = 0x29

    psadbw16 = get_abs(t0 - 0x00) + get_abs(t1 - s[1]) + get_abs(t2 - s[0]) + get_abs(t3 - s14) + get_abs(t4 - s[6]) + get_abs(t5 - s[5]) + get_abs(t6 - s[4]) + get_abs(t7 - s[3])
    psadbw17 = get_abs(t8 - 0x00) + get_abs(t9 - s[13]) + get_abs(t10 - s[12]) + get_abs(t11 - s[11]) + get_abs(t12 - s[10]) + get_abs(t13 - s[9]) + get_abs(t14 - s[8]) + get_abs(t15 - s[7])

    solver.add(psadbw16 == 0x270)
    solver.add(psadbw17 == 0x211)

    t7  = 0x70
    t15 = 0x11

    if solver.check() == sat:
        m = solver.model()
        print m

        flag = ''
        for i in s:
            flag += chr(m[i].as_long())

        print "\nflag: flag{%s}" % flag
    else:
        print "Not found."



if __name__ == '__main__':
    main()
$ python solve.py
[s_0 = 52,
 s_6 = 95,
 s_2 = 51,
 s_3 = 97,
 s_4 = 108,
 s_10 = 51,
 s_11 = 95,
 s_8 = 48,
 s_7 = 109,
 s_9 = 100,
 s_5 = 122,
 s_12 = 121,
 s_13 = 48,
 s_1 = 114]

flag: flag{4r3alz_m0d3_y0}

まとめ

z3の書き方,よくわからない部分が多い.

解けなかった問題に関しては,

pilot (Pwn75):オーバーフローさせてシェルコード実行させようとしたらできなくて,gdbで見てみたらシステムコールを呼ぶ直前でコードが変わるという謎の現象が起きたりしてた.よくわからん.

Missed Registration (Forensics150):nの値とxの値 is 何.末尾に何か付いてたけどわからん.

Gopherz (Reversing350):Gopherというプロトコルを初めて知った.なんの成果も得られませんでした.