diff options
| author | nsfisis <nsfisis@gmail.com> | 2025-04-09 20:28:28 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2025-04-09 22:24:37 +0900 |
| commit | 1e2f2e68286ce018c57945ab3cdcbaaf484377ee (patch) | |
| tree | 87f160362cafc79c7e45ebb3415fe6ef9524151c /vhosts/blog/content/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2.ndoc | |
| parent | dbc3e0dfd893435f31cca39873f7ba9bf13b93a6 (diff) | |
| download | nsfisis.dev-1e2f2e68286ce018c57945ab3cdcbaaf484377ee.tar.gz nsfisis.dev-1e2f2e68286ce018c57945ab3cdcbaaf484377ee.tar.zst nsfisis.dev-1e2f2e68286ce018c57945ab3cdcbaaf484377ee.zip | |
feat(blog/content): convert from .ndoc to .dj
Diffstat (limited to 'vhosts/blog/content/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2.ndoc')
| -rw-r--r-- | vhosts/blog/content/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2.ndoc | 171 |
1 files changed, 0 insertions, 171 deletions
diff --git a/vhosts/blog/content/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2.ndoc b/vhosts/blog/content/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2.ndoc deleted file mode 100644 index e1305179..00000000 --- a/vhosts/blog/content/posts/2022-11-19/phperkaigi-2023-unused-token-quiz-2.ndoc +++ /dev/null @@ -1,171 +0,0 @@ ---- -[article] -uuid = "10fe9c47-7029-4874-82bd-b4dc50e07809" -title = "PHPerKaigi 2023: ボツになったトークン問題 その 2" -description = "来年の PHPerKaigi 2023 でデジタルサーカス株式会社から出題予定のトークン問題のうち、ボツになった問題を公開する (その 2)。" -tags = [ - "php", - "phperkaigi", -] - -[[article.revisions]] -date = "2022-11-19" -remark = "公開" ---- -<article> - <section id="intro"> - <h>はじめに</h> - <p> - 2023 年 3 月 23 日から 25 日にかけて開催予定 (記事執筆時点) の <a href="https://phperkaigi.jp/2023/">PHPerKaigi 2023</a> において、 - 昨年と同様に、弊社 <a href="https://www.dgcircus.com/">デジタルサーカス株式会社</a> からトークン問題を出題予定である。 - </p> - <p> - 昨年のトークン問題の記事はこちら: <a href="/posts/2022-04-09/phperkaigi-2022-tokens/">PHPerKaigi 2022 トークン問題の解説</a> - </p> - <p> - すでに 2023 年用の問題は作成済みであるが、その制作過程の中でいくつかボツ問ができた。せっかくなので、PHPerKaigi 開催を待つ間に紹介しようと思う。 - </p> - <p> - 10 月から 2 月まで、毎月 1 記事ずつ公開していく予定 (忘れていなければ)。 - </p> - <p> - その 1 はこちら: <a href="/posts/2022-10-23/phperkaigi-2023-unused-token-quiz-1/">PHPerKaigi 2023: ボツになったトークン問題 その 1</a> - </p> - </section> - <section id="quiz"> - <h>問題</h> - <p> - 注意: これはボツ問なので、得られたトークンを PHPerKaigi で入力してもポイントにはならない。 - </p> - <codeblock language="php"> - <![CDATA[ - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - ]]> - </codeblock> - <p> - "And Then There Were None" (そして誰もいなくなった) と名付けた作品。変則 quine (自分自身と同じソースコードを出力するプログラム) になっている。 - </p> - </section> - <section id="how-to-obtain-token"> - <h>トークン入手方法</h> - <p> - 実行してみると、次のような出力が得られる。 - </p> - <codeblock language="php"> - <![CDATA[ - # - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - ]]> - </codeblock> - <p> - 1 行目を除き、先ほどのコードとほぼ同じものが出てきた。もう一度実行してみる。 - </p> - <codeblock language="php"> - <![CDATA[ - # - W - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - <?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s='<?php printf((isset($s)?fn($s)=>trim($s,""):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - ]]> - </codeblock> - <p> - 今度は 2 行目が書き換えられた。すべての行が変化するまで繰り返すと次のようになる。 - </p> - <codeblock language="php"> - <![CDATA[ - # - W - E - L - O - V - E - P - H - P - ]]> - </codeblock> - <p> - トークン「#WELOVEPHP」が手に入った。 - </p> - </section> - <section id="commentary"> - <h>解説</h> - <p> - 一見すると同じ行が 10 行並んでいるだけなのにも関わらず、なぜそれぞれの行で出力が変わるのか。ソースコードをコピーして、適当なエディタに貼り付けるとわかりやすい。 - </p> - <p> - Vim で開くと次のようになる (1 行目を抜粋)。 - </p> - <codeblock language="php"> - <![CDATA[ - <?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s='<200b><?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>')."\n","\x27$s\x27");?> - ]]> - </codeblock> - <p> - <code><200b></code> と表示されているのは、Unicode の U+200b で、ゼロ幅スペースである。 - </p> - <note> - <p> - エディタによっては、ゼロ幅スペースが見えないことがある。VSCode ではブラウザと同様に不可視だった。 - </p> - </note> - <p> - 文字列リテラルの中にゼロ幅スペースを仕込むことで、見た目を変えずに情報をエンコードすることが可能となる。 - </p> - <p> - 続いて、トークンへの変換ロジックを解析する。注目すべきはこの部分だ。以下、ゼロ幅スペースは Vim での表示に合わせて <code><200b></code> と記載する。 - </p> - <codeblock language="php"> - <![CDATA[ - fn($s)=>chr(strlen($s)/3) - ]]> - </codeblock> - <p> - PHP の <code>strlen()</code> は文字列のバイト数を返す。1 行目の <code>$s</code> は以下の内容となっており、 - </p> - <codeblock language="php"> - <![CDATA[ - $s='<200b><?php printf((isset($s)?fn($s)=>trim($s,"<200b>"):fn($s)=>chr(strlen($s)/3))($s=%s)."\n","\x27$s\x27");?>' - ]]> - </codeblock> - <p> - このソースコードは UTF-8 で書かれているので、105 バイトになる。それを 3 で割ると 35 となり、これは <code>#</code> の ASCII コードと一致する。他の行も、同様にしてゼロ幅スペースを詰めることで文字列長を調整し、トークンをエンコードしている。 - </p> - <p> - デコード部以外の部分は、quine のための記述である。 - </p> - </section> - <section id="outro"> - <h>おわりに</h> - <p> - <a href="https://blog.rust-lang.org/2021/11/01/cve-2021-42574.html">CVE-2021-42574</a> に着想を得た作品。この脆弱性は、Unicode の制御文字である left-to-right mark と right-to-left mark を利用し、ソースコードの実際の内容を欺く、というもの。簡単のためゼロ幅スペースを用いることとし、ついでに quine にもするとこうなった。 - </p> - <p> - ボツになった理由は、ゼロ幅スペースを表示してくるエディタが想像以上に多かったため。「同じ行が並んでいるだけなのに出力が異なる」というアイデアの根幹を崩されてしまうので、この問題は不採用となった。 - </p> - </section> -</article> |
