記号難読化シェル芸のやべーやつを解読してみた

_(){ __=$@;};_ /*/$$/../?????-*;_ ${__##*-};$(${__%?????} '!-:' ^-~<<<"'\$${#__}(")
kanata約5年前に追加

2019年2月は、記号で作る難読化シェル芸がメガシンカした歴史的な月でした。
一度にたくさんの素敵な情報が集まって、私の思考回路はショート寸前です。

記号と英字2文字だけでbash
https://www.ryotosaito.com/blog/?p=178

記号だけでシェルは操れた
https://www.ryotosaito.com/blog/?p=194

34C3 CTF minbashmaxfun - writeup
https://hack.more.systems/writeup/2017/12/30/34c3ctf-minbashmaxfun/

そんな中やべーやつが開発されます。yamayaさんという方が作成した記号だけのdateです。

$ _(){ __=$@;};_ /*/$$/../?????-*;_ ${__##*-};$(${__%?????} '!-:' ^-~<<<"'\$${#__}(")
2019年  2月 28日 木曜日 12:37:56 JST

こんなに短いのに全て記号で、そしてちゃんとdateとして動作します。美しい…そして恐ろしい…
人の褌で相撲を取るようで大変恐縮なのですが、これが埋もれてしまうのは人類の損失のような気がしたので、解読してみました。

大きく、4パートに分かれます。

$ _(){ __=$@;};_ /*/$$/../?????-*;_ ${__##*-};$(${__%?????} '!-:' ^-~<<<"'\$${#__}(")
  ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       ①              ②              ③                        ④

①関数_の定義

_(){ __=$@;}

関数_を定義しています。$@が特殊なシェル変数になります。動作は「変数__に関数の全引数の内容を設定する」という動作になっています。

②一意なディレクトリパスの取得

_ /*/$$/../?????-*

さっそく関数_が実行されています。引数に渡されている /*/$$/../?????-* は、ワイルドカード(glob)てすね。
/*/$$/../?????-* に一致するディレクトリは1つだけになりました。
/proc/[PID]/../sysrq-trigger というファイルが該当します。これはマジックSysRqキーと呼ばれるカーネル機能を制御するためのインタフェースになりますね。この機能を利用するわけではなく、triggerという文字列を取得するのが目的になっています。

※マジックSysRqキーは、カーネルのデバッグなどに使用されるやつです。昔、クラスタの動作確認するために、これを使って意図的にカーネルパニックさせた記憶があります。

③"trigger"の取得

_ ${__##*-}

ここから変数展開を使って-以降だけを切り取って、再び変数__に設定しています。
"trigger"で7文字になるのですが、この7という数字も最後に使われます。

④trでdateコマンドを生成する

ここは更に細かく分けました。

 $(  ${__%?????} '!-:' ^-~ <<<"'\$${#__}("   )
^^^^ ^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^
④-1    ④-2        ④-3       ④-4        ④-1

④-1

$( )

コマンド置換ですね。最終的にこのカッコの中がdateになれば、dateが実行されます。

④-2

${__%?????}

変数展開の機能を巧みに使って先頭ニ文字を切り出しています。triggerの頭ニ文字、trが取得されます。

④-3

'!-:' ^-~

trの範囲指定を巧みに使っています。!-: と ^-~ どいういう範囲でしょうか。
Asciiコードから確認してみます。

0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F
space ! " # $ % & ' ( ) * + , - . /
0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3A 0x3B 0x3C 0x3D 0x3E 0x3F
0 1 2 3 4 5 6 7 8 9 : ; < = > ?
0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 0x49 0x4A 0x4B 0x4C 0x4D 0x4E 0x4F
@ A B C D E F G H I J K L M N O
0x50 0x51 0x52 0x53 0x54 0x55 0x56 0x57 0x58 0x59 0x5A 0x5B 0x5C 0x5D 0x5E 0x5F
P Q R S T U V W X Y Z [ \ ] ^ _
0x60 0x61 0x62 0x63 0x64 0x65 0x66 0x67 0x68 0x69 0x6A 0x6B 0x6C 0x6D 0x6E 0x6F
` a b c d e f g h i j k l m n o
0x70 0x71 0x72 0x73 0x74 0x75 0x76 0x77 0x78 0x79 0x7A 0x7B 0x7C 0x7D 0x7E
p q r s t u v w x y z { | } ~

ここから考えると !-: は、0x21から0x3Aの範囲、^-~ は、0x5Eから0x7Eの範囲になります。
trで変換される変換表を作ってみます。

! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 :
^ _ ` a b c d e f g h i j k l m n o p q r s t u v w

dateの変換前の文字は '$7( ですね。

④-4

<<< "'\$${#__}("

<<<は、ヒアストリングという機能になります。通常、変換したい文字はパイプ経由でtrに渡しますが、これでも渡すことができます。

渡している文字は"'\$${#__}("です。この中の ${#__} は"trigger"の文字数を返す変数展開です。7ですね!

ゴールが見えてきました。

①〜④まとめ

まとめると、以下になります。

$ _(){ __=$@;}        # ①関数_の定義
$ _ /*/$$/../?????-*  # ②一意なディレクトリパスの取得
$ echo $__
/proc/16120/../sysrq-trigger
$ _ ${__##*-}         # ③"trigger"の取得
$ echo $__
trigger
$ echo ${__%?????}    # ④-2 "trigger"からtrを取り出す
tr
$ echo ${__%?????} '!-:' ^-~ # 結果としてtrが実行できる
tr !-: ^-~
$ echo ${#__}         # ④-3 "trigger"の文字数から7を生み出す
7
$ cat <<<"'\$${#__}(" # ④-4 ヒアストリングでtrの入力にする
'$7(
$ $(${__%?????} '!-:' ^-~<<<"'\$${#__}(")   # ④-1 $(tr '!-:' ^-~ <<< "'\$7(" ) を実行
2019年  2月 28日 木曜日 12:54:22 JST

おわかりいただけたでしょうか…
ここまでくるともはや芸術品...!


出典は、ココのコメントにあります。
記号だけで Hello world したやべーやつもありますねw


2019.3.7 yamayaさん新作が以下になります

$ : /*/$$/../?????-*;: ${_##*-};$(${_%?????} '!-:' ^-~<<<"'\$${#_}(")
2019年  3月  7日 木曜日 22:51:09 JST

より短い!なぜこれでdateが実行できるか解読してみよう!


コメント

クリップボードから画像を追加 (サイズの上限: 100 MB)