SECCIN 2015 online CTF の記事はこちら
SECCON2016は、すっかり忘れていて気づいたら過ぎてました・・・。
しかも後から問題が公開されていない模様。後追いもできず。
※と思ったら、アップされている!探し方が悪かったよう。こちらも後からやってみる!!!
SECCON2016_online_CTF
後追い記事はぼちぼち書く(&やってみる)として、今回は初めて参加できたので早めにwrite-upを。
はじめに
この記事は SECCON 2017 online CTF に参加した際の記録になります。
SECCON online 初参加記念。
- 簡単な問題しか解けていませんのでご了承ください
- ネタバレなので閲覧の際はご注意ください
- write-upの記事に後追いの記録を徐々に追加更新予定
基本装備
- MacBookAir(OS X 10.9くらい) 2011モデル
- ruby 2.3.0
- python 2.7.9, 3.1.0(使わなかった)
- GIMP 2.8.10
- JpegAnalyzer Plus
- 0xED(バイナリエディタ)
- wine
- Ubuntu14.04 VM環境
そうです!2015の時からほとんど変わっていないです!
イベントの時だけ頑張る君になりつつあります。
そろそろMacBookAir買い換えたい・・・
Vigener3d (Crypto) 100
すぐにタイトルでググるといい、くらいの記憶はあった。
Vigenere暗号、というのがあるらしい。
ヴィジュネル暗号
通常は2次元の表で、平文に対して鍵が1つなのだけど、今回の問題は平文1つに対して鍵が2つの3次元。
ビジュアルに頼りがちな私としては3次元までなら頭で対応表をイメージしやすい・・・
が、問題を解くにはプログラムに落とさねば。
問題
Vigenere3d ----- Vigenere3d.py import sys def _l(idx, s): return s[idx:] + s[:idx] def main(p, k1, k2): s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_{}" t = [[_l((i+j) % len(s), s) for j in range(len(s))] for i in range(len(s))] i1 = 0 i2 = 0 c = "" for a in p: c += t[s.find(a)][s.find(k1[i1])][s.find(k2[i2])] i1 = (i1 + 1) % len(k1) i2 = (i2 + 1) % len(k2) return c print main(sys.argv[1], sys.argv[2], sys.argv[2][::-1]) ----- $ python Vigenere3d.py SECCON{**************************} ************** POR4dnyTLHBfwbxAAZhe}}ocZR3Cxcftw9
解き方
問題のソースを見ると、平文がp
, 鍵はk1
とk2
で、k2
はk1
の逆であることがわかる。
なので、結局上記のコードのk2
はk1
で置き換えて表現することができ、平文1:鍵1の問題として解くことができる。
すでにわかっている平文は7文字、鍵は14文字なので、既にわかっているものから鍵を推測する。
サンプルコード
#!/usr/bin/env python s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_{}" C = 'POR4dnyTLHBfwbxAAZhe}}ocZR3Cxcftw9' predef = 'SECCON{' K = [] for i in range(len(predef)): K.append(s[(len(s) + s.find(C[i]) - s.find(predef[i])) % len(s)]) K = K + K[::-1] print "key= " + ''.join(K) i = 0 P = '' for a in C: P += s[(len(s) + s.find(a) - s.find(K[i])) % len(s)] i = (i + 1) % len(K) print P
flag: SECCON{Welc0me_to_SECCON_CTF_2017}
Run me! (Programing) 100
ほとんどのチームが解けていたっぽい問題
問題
Run me! ----- RunMe.py import sys sys.setrecursionlimit(99999) def f(n): return n if n < 2 else f(n-2) + f(n-1) print "SECCON{" + str(f(11011))[:32] + "}" -----
ヒント
このプログラムの構造は再帰を使用しているが、なんだかメモリ・CPUを沢山食いそうだ・・・
試しにn
を 3~7くらいで計算すると、見たことのある数列になるかも
解き方
大変シンプルなスクリプト。
漸化式を再帰で表現方式で、記述がシンプルになるのは良いのだが処理効率が低下してしまうので
普通にn=1から計算をしていって前の計算結果を使う、というようなプログラムにした方が俄然効率が良い。
後から調べたらこのページがわかりやすかった。
http://www.kogures.com/hitoshi/webtext/al-recursive/index.html
今回のプログラムはフィボナッチ数列なので、最後のケースにそのまま当てはまる。
計算量の比較はこのページの最後に書いてある通り
n=30のときでは、f2ならば90回なのに、f1(再帰)では(n=30のフィボナッチ数は)832,040回になってしまいます
サンプルコード
#!/usr/bin/env python import sys NUM = 11011 def f(N): F[0] = 1 F[1] = 1 for i in range(2, N): F[i] = F[i-2] + F[i-1] return F[N-1] # main print "SECCON{" + str(f(NUM))[:32] + "}"
flag: SECCON{65076140832331717667772761541872}
putchar music (Programming) 100
これもとにかくググる
http://royal-paw.com/2012/01/bytebeats-in-c-and-python-generative-symphonies-from-extremely-small-programs/
こんなサイトが出てきた。ググるの大事。
問題
putchar music This one line of C program works on Linux Desktop. What is this movie's title? Please answer the flag as SECCON{MOVIES_TITLE}, replace all alphabets with capital letters, and spaces with underscores.
main(t,i,j){unsigned char p[]="###<f_YM\204g_YM\204g_Y_H #<f_YM\204g_YM\204g_Y_H #+-?[WKAMYJ/7 #+-?[WKgH #+-?[WKAMYJ/7hk\206\203tk\\YJAfkkk";for(i=0;t=1;i=(i+1)%(sizeof(p)-1)){double x=pow(1.05946309435931,p[i]/6+13);for(j=1+p[i]%6;t++%(8192/j);)putchar(t>>5|(int)(t*x));}}
解法
Linuxと問題文にあるので、とにかくLinux上で作業をする。 とりあえず問題文のコンパイルを通す。 warnは無視。
#include <stdio.h> #include <math.h> int main(void){ int i = 0; int j = 0; int t = 1; unsigned char p[]="###<f_YM¥204g_YM¥204g_Y_H #<f_YM¥204g_YM¥204g_Y_H #+-?[WKAMYJ/7 #+-?[WKgH #+-?[WKAMYJ/7hk¥206¥203tk¥¥YJAfkkk"; for(i=0;t=1;i=(i+1)%(sizeof(p)-1)){ double x=pow(1.05946309435931,p[i]/6+13); for(j=1+p[i]%6;t++%(8192/j);) putchar(t>>5|(int)(t*x)); } return 0; }
上記、putchar musicのサイトを読むとc言語の場合はあとSoXというのを使えば鳴りそうだ。
How To Install SOX 14.4.1 On Ubuntu 14.10, Ubuntu 14.04 And Derivative Systems
これを参考にinstall。ありがたや。
なんの捻りもなく、言われるがままにコンパイル・実行
$ gcc -o bytebeat PutCharMusic.c -lm $ ./bytebeat | sox -r 8000 -b 8 -c 1 -t raw -s - -d
音楽が鳴った!すごい!ブツブツ切れてるけど・・・
flag: SECCON{STAR_WARS}
SHA-1 is dead (Crypto) 100
SHA-1がすでに安全ではなくなったよ、系の問題と思われる。
問題
SHA-1 is dead http://sha1.pwn.seccon.jp/ Upload two files satisfy following conditions: file1 != file2 SHA1(file1) == SHA1(file2) SHA256(file1) <> SHA256(file2) 2017KiB < sizeof(file1) < 2018KiB 2017KiB < sizeof(file2) < 2018KiB * 1KiB = 1024 bytes
下調べ
下記の条件を満たすfile1,2を作成する。 * file1とfile2は異なる * SHA-1が一致(衝突) * SHA-256は異なる * 2017KiB より大きく 2018KiB より小さい
完全にSHA-1はもうダメだという問題ですね。
危ないからもう使えない、という知識くらいはあったものの、実際にどうやったら衝突するものを自由に作れるのか?は考えたことがなかった。
のでググってみると、2017年2月24日の記事で
米Googleとオランダの研究機関CWI Instituteは23日、 2つの異なるファイルから同じSHA-1ハッシュ値を生成する“衝突”に成功し、 その手法とハッシュ値が同一の2つのPDFファイルを公開した。 Googleではより安全なSHA-256やSHA-3への移行を推奨している。
というのを発見。
記事はこちら
タイムリーな話題だったんですね。
元ネタはこちらだそう。
解法
もうダメだの理由が、計算機の発展に伴って衝突するペアを見つけるのにかかる時間が減ったから、
だけだとすると、この貧弱なPCで処理をぶん回して衝突するペアを探すのは厳しい。
もう少し探して見ると、こんな記事とツールが(一瞬で出て来たけど)。
SHA-1ハッシュの衝突を現実的な時間で生成する攻撃「Shatterd」
Shattered攻撃は多数の暗号解析技術を組み合わせたもので、同じSHA-1ハッシュ値を持ち、内容の異なる2つのPDFファイルの生成などが可能だ。高速といっても263回の試行が必要となり、攻撃の第1フェーズは6,500 CPUで1年間、第2フェーズは110 GPUで1年間を要する。それでもブルートフォース攻撃と比較すると10万倍以上高速だという。
ふむふむ。ってえええ!2年も待てませんよ。
ということで、
shatterd.ioではPoCとして、同じSHA-1ハッシュ値で内容の異なる2つのPDFファイル (PDF 1/PDF 2)を公開している
このPDFたちを改造してやるのが良さそう。 それぞれDLしてみると、422KB(422435 byte)ずつ。
そしてこんなサイトを見つけてしまった。
巷で話題のGoogleのSHA-1衝突やってみた
とてもわかりやすい。ここが大事
先頭320バイトの部分で衝突が起きていてそれ以降が同じ値ならずっと衝突し続けるとのことです
ということは、足りないバイト数を同じ値で埋めてやれば衝突した指定のサイズのファイルが作成できそう。
ファイル形式についての指定はないので、PDFファイルであることは捨てて良い。
ファイルの末尾に適当なものを付け足すスクリプトを書こうかとも思ったのだけど、以前linuxやwindowsコマンドでランダムな内容の任意のサイズのファイルを生成するのがあったのを思い出し、今回も何かあるのではないかと調べて見る。
容量指定のダミーファイルを作成したい
あった。
これで適当なサイズのファイルを作成し、元のshattered-1(2).pdf
にくっつけてやれば良さそう。
適当なサイズは、くっつけた後に2017kb<ファイルサイズ<2018kb
になる必要があるので、下記計算でサイズを出しておく。
(2017 * 1024 + 1) - 422435 = 1642974
以下、実行コマンド羅列
$ dd if=/dev/zero of=tempfile bs=1 count=1642974 1642974+0 records in 1642974+0 records out 1642974 bytes transferred in 7.541782 secs (217850 bytes/sec) $ cat shattered-1.pdf tempfile > file1 $ cat shattered-2.pdf tempfile > file2 $ ll -rw-r--r-- 1 *** staff 2065409 ** ** 09:33 file1 -rw-r--r-- 1 *** staff 2065409 ** ** 09:33 file2 $ openssl sha1 file1 SHA1(file1)= d5b192f19ef498b739a3ecd0498e20f44e4fa0c9 $ openssl sha1 file2 SHA1(file2)= d5b192f19ef498b739a3ecd0498e20f44e4fa0c9
ということで、作成したファイルが本当にSHA1が一致していることが確認できました。
これを問題のサイトに突っ込めばflagゲットです。
flag: SECCON{SHA-1_1995-2017?}
SqlSRF (Web) 400
4・・・400点問題だから無理かなぁ・・・とは思いつつ、せっかくリアルタイムで参加できたのでWeb系の問題を中心に見てみることに
問題
SqlSRF The root reply the flag to your mail address if you send a mail that subject is "give me flag" to root. http://sqlsrf.pwn.seccon.jp/sqlsrf/
足跡
まずはググる。SQL関連のワードでSRFというと
Set Returning Functions
というのがヒットする
http://sqlsrf.pwn.seccon.jp/sqlsrf/
にアクセスすると、以下のページが表示される
index.cgiにアクセスすると、ログイン画面が表示。ログイン情報を持っていないと、menu.cgiにアクセスしても同じようだ。
適当にlogin名を入れてloginボタンを押して見ると、こんなメッセージが。
あとは、index.cgi_backup20171129
にアクセスして見ると、index.cgiと思われるperlのソースコードが。
#!/usr/bin/perl use CGI; my $q = new CGI; use CGI::Session; my $s = CGI::Session->new(undef, $q->cookie('CGISESSID')||undef, {Directory=>'/tmp'}); $s->expire('+1M'); require './.htcrypt.pl'; my $user = $q->param('user'); print $q->header(-charset=>'UTF-8', -cookie=> [ $q->cookie(-name=>'CGISESSID', -value=>$s->id), ($q->param('save') eq '1' ? $q->cookie(-name=>'remember', -value=>&encrypt($user), -expires=>'+1M') : undef) ]), $q->start_html(-lang=>'ja', -encoding=>'UTF-8', -title=>'SECCON 2017', -bgcolor=>'black'); $user = &decrypt($q->cookie('remember')) if($user eq '' && $q->cookie('remember') ne ''); my $errmsg = ''; if($q->param('login') ne '') { use DBI; my $dbh = DBI->connect('dbi:SQLite:dbname=./.htDB'); my $sth = $dbh->prepare("SELECT password FROM users WHERE username='".$q->param('user')."';"); $errmsg = '<h2 style="color:red"> Login Error!</h2> '; eval { $sth->execute(); if(my @row = $sth->fetchrow_array) { if($row[0] ne '' && $q->param('pass') ne '' && $row[0] eq &encrypt($q->param('pass'))) { $s->param('autheduser', $q->param('user')); print "<scr"."ipt>document.location='./menu.cgi';</script>"; $errmsg = ''; } } }; if($@) { $errmsg = '<h2 style="color:red"> Database Error!</h2> '; } $dbh->disconnect(); } $user = $q->escapeHTML($user); print <<"EOM"; <!-- The Kusomon by KeigoYAMAZAKI, 2017 --> <div style="background:#000 url(./bg-header.jpg) 50% 50% no-repeat;position:fixed;width:100%;height:300px;top:0;"> </div> <div style="position:relative;top:300px;color:white;text-align:center;"> <h1> Login</h1> <form action="?" method="post"> $errmsg <table border="0" align="center" style="background:white;color:black;padding:50px;border:1px solid darkgray;"> <tr><td>Username:</td><td><input type="text" name="user" value="$user"></td></tr> <tr><td>Password:</td><td><input type="password" name="pass" value=""></td></tr> <tr><td colspan="2"><input type="checkbox" name="save" value="1">Remember Me</td></tr> <tr><td colspan="2" align="right"><input type="submit" name="login" value="Login"></td></tr> </table> </form> </div> </body> </html> EOM 1;
※競技中はperlは触ったことがほとんどないので、これは何の言語だ?とぱっとわからなかったが、落ち着いて見て見ると1行目に書いてある・・・。競技中はだいぶテンパっていた様子。
どうやらログイン時のUsername
の値を、直接DBの検索クエリに使用している。ここが一つ攻撃ポイントっぽい。
my $sth = $dbh->prepare("SELECT password FROM users WHERE username='".$q->param('user')."';");
試しに、sql injectionの簡単なやつをUsername欄に入れてloginしてみる
Username: kusuwada"' OR 1 = 1'"
Password:
こんな画面が現れた。エラーメッセージが変わった。
SQL injectionできてるようだ。
この先のコードを読んでいくと、認証が成功するとmenu.cgiに飛べることがわかる
if(my @row = $sth->fetchrow_array) { if($row[0] ne '' && $q->param('pass') ne '' && $row[0] eq &encrypt($q->param('pass'))) {
この条件式をクリアすれば認証成功。
上のSELECT文で引っ張ってきた値が、入力画面のPassword
に入れた値をencrypt
関数で暗号化したものと一致すれば良い。
普通のログイン処理だと失敗してしまうことから、DBに保存されているpass
は平文か、もしくはencrypt
関数以外で暗号化されているようだ。
そこのロジックはもはやわからないので、"SELECT文で引っ張ってきた値"が"入力画面のPassword
に入れた値をencrypt
関数で暗号化したもの"になるようにSQL Injectionする。
ということで、どうも次ののstepにいくためには、平文のpass
と暗号化したpass
を入手すことが必要なようだ。
どうやったら手に入るのか、ヒントを探す。
ソースで呼ばれているencrypt
関数は、別管理らしく公開されていない。
$q->cookie(-name=>'CGISESSID', -value=>$s->id), ($q->param('save') eq '1' ? $q->cookie(-name=>'remember', -value=>&encrypt($user), -expires=>'+1M') : undef)
ここから、save
が1
の時、cookieに remember
:&encrypt($user)
がsetされるらしいということがわかる。
$userはフォームのUsernameに入れた値なので、RememberMeにチェックをつけて(save==1にするため)login、remember
cookieをみれば暗号文が取得できる。
同じencrypt関数が使われているので、userだろうがpassだろうが、平文と暗号文の対応が取れればOK。
さらに、
$user = &decrypt($q->cookie('remember')) if($user eq '' && $q->cookie('remember') ne '');
ここから、Usernameが空で、remember
のcookieが何かしら書かれている場合、$user
にremember
cookieを復号したものが入ることがわかる。
おお〜。400点問題でも案外楽しめている。
ここで、手順を再整理
目的とまとめ
- 認証を通してmenu.cgiに飛ぶ
- 認証を通すためには、"SELECT文で引っ張ってきた値が、入力画面の
Password
に入れた値をencrypt
関数で暗号化したものと一致"させる必要がある - 入力画面の
Username
はSQL Injection可能なので、SELECT文で引っ張って来る値が所望のものになるようにUsernameを決める - SQL Injectionなんて実践することないので、クエリの組み立て方はググる。 Google検索: sql injection 一覧 検索ワードに日本語混じっているにもかかわらず、9万件以上のヒット。凄い。 トップにあったページを参照 http://www.byakuya-shobo.co.jp/hj/moh/sqlinjectioncheatsheet.html 自分の入れたテキストを返してもらえそうなやつを探す
手順
- 適当なパスワードを決めて、それの暗号文を取得
今回はkusuwada
にすることに。
Username:kusuwada
RememberMe: ON
でloginしてremember
のcookieを確認 ->encrypt("kusuwada")
取得 .2. 認証を通してlogin
Username:kusuwada ' UNION SELECT "{encrypt("kusuwada")}" --
Password:kusuwada
でlogin
cookieの確認方法。こういうことはしばらくやっていないとすぐ忘れてしまう・・・。
この辺の記事を参考に
1 の手順で、remember
:743f7c045d8853660c482b84f487e01f
がcookieから得られる
2 の手順で
おお、menu.cgiに移行した!ああ、もう満足。
この画面上のnetstat -tnl
ボタンを押すと、こんな感じでnetstat結果が取得できる
localの25番ポートが開いているようなので、メールが送れそう。
2.
のフォームはadminのみが使えるよ、ということで、adminとしてログインし直す必要があるようだ。
いまは' UNION SELECT "743f7c045d8853660c482b84f487e01f" --
とかいうUsernameになっているが、ここがadminになるようにPasswordを探し出すのが次のミッション。
encrypt
関数に対して、上で書いたように
* RememberMeにチェックをつけて(save==1にするため)login、remember
cookieをみれば暗号文が取得できる
* Usernameが空で、remember
のcookieが何かしら書かれている場合、$user
にremember
cookieを復号したものが入る
平文->暗号文->平文が自由に取得できることがわかる。今求めたいのはadmin
という暗号文に対する平文なので、一筋縄ではいかない。
ここで時間切れ。
解法
★後追いでやりたいなぁ
Log search (Web) 100
これも後追いできない可能性が高いので、すぐに着手。
問題
Log search Search the flag on Elasticsearch. http://logsearch.pwn.seccon.jp/
足跡
http://logsearch.pwn.seccon.jp/
の下の方にある、Log search
を踏むと、 /logsearch.php
に飛ぶ。
searchに検索パスを入れてそれにマッチするログの履歴が出るようになっているようだ。
検索結果の項目は timestamp, method, request, response
解き方
問題文に Search the flag on Elasticsearch.
とあったので、素直に flag
でまずはログを検索してみる
沢山結果が出てくるが、responseが200
のもののみpickupするようにして何度か文言を変えて検索していくと
/flag-****.txt
というpathでresponseが200の物を発見。
http://logsearch.pwn.seccon.jp/flag-****.txt
に実際にアクセスしてみると、テキストでflagが出てきました。
flag: SECCON{N0SQL_1njection_for_Elasticsearch!}
後から
flagをかなり早い段階でgetできたものの、flag
NOSQL injection for Elasticsearch
と書いてある。(ほぼflagそのまま・・・)
私のやったことといえば、どちらかというとディレクトリ・トラバーサルのような。
もしかしたら運が良かっただけで、本来の解法ではないのかもしれない。
※ logが個人に閉じていない全員分のアクセスログで、たまたま前にflagをgetした人のが見えた可能性・・・
後追いで上記の想定でやって見たい気もするが、いつサーバー閉じられるんだろう。
JPEG file (Binary) 100
ステがノグラフィがなくなったので、せめてこれだけは・・・!
結構後の方でチェックしたら、多くの人が解けている。いけるかも!
問題
JPEG file Read this JPEG is broken. It will be fixed if you change somewhere by 1 bit. tktk-892009a0993d079214efa167cda2e7afc85e6b9cb38588cba9dab23eb6eb3d46
と、JPEGファイルが添付されていました。
足跡 & 解法
問題文より、1bit反転するだけで読めるようになるのか。 とりあえずfileコマンド
$ file tktk-892009a0993d079214efa167cda2e7afc85e6b9cb38588cba9dab23eb6eb3d46.jpg tktk-892009a0993d079214efa167cda2e7afc85e6b9cb38588cba9dab23eb6eb3d46.jpg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 192x192, segment length 16, baseline, precision 8, 339x53, frames 3
一般的なJpeg imageのようだ。
素直に開いてみると、黒い帯状。JpegAnalyzerで開いてみると、サムネイル画像は一切なし。特にAnalyzer側で壊れているところは検出されず。
1bit反転で読めるようになるということは、データ部分じゃなくてマーカーか何かが誤検出されているっぽい。(データ部分だとすると1bit反転したくらいでは大勢に影響がないため)
完全に勘だけど、imageの形状的に修復すれば絵でflagが表示されるっぽい。
とりあえず壊れているところのヒントを得るために、imagemagickに食わせてみる。
$ convert -geometry 70% tktk-892009a0993d079214efa167cda2e7afc85e6b9cb38588cba9dab23eb6eb3d46.jpg resize70.jpg convert: Corrupt JPEG data: premature end of data segment `tktk-892009a0993d079214efa167cda2e7afc85e6b9cb38588cba9dab23eb6eb3d46.jpg' @ warning/jpeg.c/JPEGWarningHandler/352. convert: Unsupported marker type 0xfc `tktk-892009a0993d079214efa167cda2e7afc85e6b9cb38588cba9dab23eb6eb3d46.jpg' @ warning/jpeg.c/JPEGErrorHandler/319.
お、壊れていると検知された。
Unsupported marker type 0xfc
ということなので、markerの識別子であるFF
をくっつけてFFFC
というのをマーカーとして誤検出してしまったっぽい。
バイナリエディタで開いてみる。
2年前からupdateされていた0xEDを使用。FFFC
を検索。一箇所だけhitしたので、ここをFEFC
に書き換え。
開いてみると、何やら壊れているが表示が変わったっぽい。修復できたか確認するために、再度imagemagickに食わせてみる。
$ convert -geometry 70% tktk-892009a0993d079214efa167cda2e7afc85e6b9cb38588cba9dab23eb6eb3d46.jpg resize70.jpg
resize70.jpgができ、無事中身を見ることができました。
Thank you for playing! (Thank you!) 100
問題
Thank you for playing! SECCON{We have done all the challenges. Enjoy last 12 hours. Thank you!}
解法
なんと、そのままフラグを入れるだけ
リアル脱出ゲームの感覚だと、「絶対何か裏があるはず・・・!」とか思ってしまうが、今回は参加ボーナスみたいなものだった模様
終わりに
個人的には2015年と比べてステガノグラフィがなくなったのがちょっとショックでしたが
1問も解けないんじゃなかろうかと思っていたので、何かしら解けて良かったです。
あとはここ半年、仕事での自分のメインの開発言語がRuby->Pythonになっていたのですが、問題もPythonが多かったり、暗号系のライブラリもPythonが充実していたので好都合でした。
大会開催日時を知ったのが今週で先約もあり、なかなか時間が取れませんでしたが、あとからまたのんびりできればなあと思います。
※後追いの辛いところは、環境がなくなってしまうことと、ググったらwire-upが出てきてしまうこと・・・