前回 は、CpawCTF で、CTF に入門しました。
今回は、以前(John the Ripperでshadowファイルのパスワードを解読してみる - 土日の勉強ノート)にやったパスワードの解読の続きをやりたいと思います。
このときは、John the Ripper というオフラインパスワード解析ツールを使ったのですが、同じようなツールとして、「hashcat」というものがあります。
hashcat は、パターンを細かく指定してパスワードを柔軟に生成させることが出来るので、とても便利なツールです。今回は、hashcat を使ってみます。
それでは、やっていきます。
はじめに
「セキュリティ」の記事一覧です。良かったら参考にしてください。
セキュリティの記事一覧
以前、John the Ripper を使ってみた記事は以下です。
daisuke20240310.hatenablog.com
今回は、同じようなツールである「hashcat」を使ってみます。hashcat は公式サイトのドキュメントが充実しています。以下です。
※注意:会社からアクセスする場合、フィルタに引っかかる可能性が高いです
hashcat.net
今回は、使い方を詳しく見ていくのと、どのパターンのパスワード解析なら、どのくらいの時間がかかるのか、というところを考えてみたいと思います。
hashcatの概要
John the Ripper と同様に、パスワードハッシュを与えると、内部で、あるパターンに沿って文字列を生成し、ハッシュを計算して、与えられたハッシュと一致すると、パスワードが解読できた、というツールです。実際にログイン画面などで試すツール(オンラインパスワード解析ツール)ではないので、オフラインパスワード解析ツール、というように呼ばれたりします。
実は、John the Ripper を使ったときは気づかなかったのですが、John the Ripper と hashcat は、どちらも GPU に対応していて、高速に解析することができます。
また、hashcat は、オープンソース(MITライセンス)で、Linux、Windows、macOS に対応していて、多くのハッシュアルゴリズムに対応しています。詳細は公式サイトのトップページを参照してください。
John the Ripper と同様に、hashcat も、ParrotOS に最初からインストールされています。
今回は、どのアルゴリズムのハッシュの場合で、どのようなパターンの解析をさせたら、どれくらの時間がかかるのか、という時間の見積もりが出来ることを目標とします。
hashcat は、公式サイトのドキュメントが充実しているので、今回は使い方をしっかり理解していきます。
今回使っていく hashcat のバージョンは、v6.2.6 です。
hashcatの簡単な使い方
まずは、John the Ripper でやったことと同じ内容をやってみます。
現在使ってる ParrotOS のデフォルトユーザは、user で、デフォルトパスワードは parrot です。
/etc/shadow からパスワードハッシュをコピーしてきました。
$ cat data/shadow.user
user:$y$j9T$1XY9PJDFr9r.ab66lidWr0$yVQTTI4D.2FkgJLif/Wzkc2v3PTYuCjXYZWjxAaPDB4:19937:0:99999:7:::
John the Ripper にはデフォルトの辞書が用意されてましたが、hashcat には、たぶんですが、ありません。ここでは、John the Ripper のデフォルト辞書を使います。
では、実際にやってみます。と思って、yescrypt を探したところ、ありませんでした。Web で検索してみましたが、hashcat は、yescrypt には対応していないということでした。yescrypt は、GPU で高速化しにくいアルゴリズムであり、hashcat は GPU をメインに考えたソフトだからとかなんとか書いてました。
John the Ripper についても、正式な対応ではなく、Linux の機能?ライブラリ?を使って対応させているとのことでした。
困ったので、別のアルゴリズムを使います。前回、mkpasswdコマンドを使って、12種類のハッシュを計算してみました(パスワードは parrot です)。その中の descrypt を使ってみます。暗号化アルゴリズムの DES を使ったパスワードチェックです(暗号化なので、正確にはハッシュではないと言われています)。
mkpasswd の実行結果を貼り付けたファイルを作りました。
$ cat data/user-des.hash
Xycb4n5QCfldM
hashcat の簡単な使い方は、以下です。
$ hashcat -m ハッシュタイプ -D CPU:1/GPU:2 -a 攻撃モード ハッシュが書かれたファイル ワードリスト
$ hashcat -h
を実行すると、使い方が出ます。長いのでここには貼りませんが、grep します。
$ cat /usr/share/wordlists/john.lst | grep parrot
parrot
$ hashcat -h | grep descrypt
1500 | descrypt, DES (Unix), Traditional DES | Operating System
では、やってみます。CPU です。
$ hashcat -m 1500 -D 1 -a 0 data/user-des.hash /usr/share/wordlists/john.lst
hashcat (v6.2.6) starting
OpenCL API (OpenCL 3.0 PoCL 3.1+debian Linux, None+Asserts, RELOC, SPIR, LLVM 15.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform
==================================================================================================================================================
* Device
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 8
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Not-Iterated
* Single-Hash
* Single-Salt
Watchdog: Temperature abort trigger set to 90c
Host memory required for this attack: 0 MB
Dictionary cache built:
* Filename..: /usr/share/wordlists/john.lst
* Passwords.: 3559
* Bytes.....: 26326
* Keyspace..: 3559
* Runtime...: 0 secs
Xycb4n5QCfldM:parrot
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 1500 (descrypt, DES (Unix), Traditional DES)
Hash.Target......: Xycb4n5QCfldM
Time.Started.....: Fri Aug 23 21:21:52 2024 (0 secs)
Time.Estimated...: Fri Aug 23 21:21:52 2024 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/john.lst)
Guess.Queue......: 1/1 (100.00%)
Speed.
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 2137/3559 (60.04%)
Rejected.........: 89/2137 (4.16%)
Restore.Point....: 1064/3559 (29.90%)
Restore.Sub.
Candidate.Engine.: Device Generator
Candidates.
Hardware.Mon.
Started: Fri Aug 23 21:20:20 2024
Stopped: Fri Aug 23 21:21:54 2024
約1分半でした。Status を見ると、Cracked と出ました。解読できたということです。ダメなときは Exhausted と出ます。
性能についても出力されています。Speed.#1.........: 5920 H/s (5.79ms)
は、1秒間に 5920個のハッシュが計算できたということです。ワードリストは 3559個が登録されていて、そのうち、2137個が有効でした。ハッシュの計算だけで言うと、1秒もかからずに計算できているということになります。
細かい結果が出力されるので、分かりやすいです。
また、結果は以下のようにすると見れます。パスワードの parrot が検出できています。
$ hashcat --show data/user-des.hash
Hash-mode was not specified with -m. Attempting to auto-detect hash mode.
The following mode was auto-detected as the only one matching your input hash:
1500 | descrypt, DES (Unix), Traditional DES | Operating System
NOTE: Auto-detect is best effort. The correct hash-mode is NOT guaranteed!
Do NOT report auto-detect issues unless you are certain of the hash type.
Xycb4n5QCfldM:parrot
hashcatの使い方
細かい使い方について、公式サイトの Wiki の Mask Attack の内容を見ながら、書いていきます。例もいくつか書かれているので、分からなくなったら、ここを見てください。
攻撃モード
まずは、攻撃モードです。攻撃モードというのは、辞書ファイルが与えられたとき、その辞書ファイルを順番に試す(辞書攻撃)モードや、アルファベットの大文字(26種)、小文字(26種)、数字(10種)の組み合わせを順番に試す(総当たり、ブルートフォース)モードなどがあります。
以下の表にまとめます。辞書と書いてますが、ワードリストのことです。
攻撃モード |
オプション |
内容 |
辞書攻撃 |
-a 0 |
リスト内のすべての単語を試す、ストレートモードとも呼ばれる |
組み合わせ攻撃 |
-a 1 |
複数の辞書を組み合わせる |
総当たり攻撃 |
-a 3 |
ブルートフォース攻撃、マスク攻撃とも呼ばれる |
ハイブリッド攻撃 |
-a 6 |
辞書+マスク攻撃 |
ハイブリッド攻撃 |
-a 7 |
マスク+辞書攻撃 |
関連付け攻撃 |
-a 9 |
いろんな情報を使用した攻撃 |
マスクについては難しいので細かく書いていきます。
マスク
マスクとは、どういうパターンでパスワードを生成して試していくのかを決めるものになります。
例えば、4桁の数字(10種)のパスワードだとすると、10の4乗の組み合わせ(10000通り)になります。0000 から 9999までなので、10000通りで合ってますね。総当たりで単純に試すと、10000回のハッシュ計算が必要になりますが、例えば、先頭は 0 の場合が多いと分かっていたとすると、残り3文字なので、10の3乗となり、1000通りで完了します。
アルファベットの場合でも、先頭を大文字にする人が多いですよね(私もですw)。その場合、8桁のアルファベットの場合、大文字小文字の両方を試すと、52の8乗(53,459,728,531,456通り)になりますが、先頭だけ大文字小文字にして、残りの7桁は小文字だけにすると、52*26の7乗(449,781,369,856通り)となり、全て大文字小文字を試す場合に比べて、約119倍速くなります。
こういう細かい指定に使えるのがマスクです。
マスクには以下の3種類が使えます。
マスク |
説明 |
備考 |
カスタム文字セット |
1、2、3、4 のいずれか |
|
組み込み文字セット |
l、u、d、s、a のいずれか |
おそらく h、H、b も可能 |
静的文字 |
?を付けず固定の文字を指定 |
?を指定したい場合は??を指定する |
ドキュメントには、組み込み文字セットは、「l、u、d、s、a のいずれか」とありましたが、コマンドのヘルプには、h と H と b が追加されていましたので、実際は、「l、u、d、h、H、s、a、b のいずれか」が正しいと思います。
組み込み文字セット
組み込み文字セットについて、以下にまとめました。
変数 |
文字セット |
内容 |
?l |
英小文字 |
abcdefghijklmnopqrstuvwxyz |
?u |
英大文字 |
ABCDEFGHIJKLMNOPQRSTUVWXYZ |
?d |
数字 |
0123456789 |
?h |
16進数小文字 |
0123456789abcdef |
?H |
16進数大文字 |
0123456789ABCDEF |
?s |
記号 |
!”#$%&'()*+,-./:;<=>?@[\]^_`{|}~ |
?a |
?l?u?d?sと同じ |
|
?b |
16進数 |
0x00 – 0xff |
記号については、32種類になります。こんなに使えたんですね、知らなかったです。
カスタム文字セット
独自に文字セットを4つ作ることができて、カスタム文字セットと言います。
以下は、おそらく、1~3 に、デフォルトで設定されているカスタム文字セットだと思います。
変数 |
文字セット |
備考 |
?1 |
?l?d?uと同じ |
英小文字、数字、英大文字 |
?2 |
?l?dと同じ |
英小文字、数字 |
?3 |
?l?d*!$@_ |
英小文字、数字、5個の記号 |
コマンドライン引数として、-1 ?l?d
などと指定します。これで、マスクに、?1
を使うと、英小文字、数字という定義になります。
デフォルトマスク
-a 3
の場合に、マスクを指定しないと、以下を指定したことになります。
?1?2?2?2?2?2?2?3?3?3?3?d?d?d?d
これは、先頭は英大文字を含めた英小文字と数字、続く6文字は英小文字と数字、続く4文字は英小文字と数字と5個の起動、末尾4文字は数字の計15文字を設定しています。
hashcatの処理時間を見積もってみる
先ほど、hashcatの簡単な使い方 のところで、処理時間の扱い方は説明したので、既に見積もる方法は分かったと思います。
あとは、ハッシュタイプによって、どれぐらいの時間がかかるのかが分かれば、およその時間は見積もれると思います。
以下のサイトにベンチマークが書かれています。
Hashcat v6.2.6 benchmark on the Nvidia RTX 4090 · GitHub
例えば、先ほどの descrypt の場合だと、以下のように書かれています。
--------------------------------------------------------
* Hash-Mode 1500 (descrypt, DES (Unix), Traditional DES)
--------------------------------------------------------
Speed.#1.........: 6276.6 MH/s (85.31ms) @ Accel:16 Loops:1024 Thr:256 Vec:1
1秒間に、6277メガ個(約62.7億個)のハッシュが計算できるそうです。凄すぎです。
例えば、記号は使わない場合、パスワードが8桁だとすると、アルファベットの大文字小文字と数字の全組み合わせは、62の8乗(218,340,105,584,896)になります。これを、6276.6メガで割ると、何秒で総当たりのパスワードが計算できます。約34,786秒(約9.7時間)です。
これに記号32種が加わると、同じ8桁とすると(DESは8桁までしか使えない)、94の8乗(6,095,689,385,410,816)となり、6276.6メガで割ると、971176秒(約270時間、約11日間)となります。思ったより現実的な時間でした。
DES による暗号化パスワードは、UNIX で使われていたそうです。ChatGPT に聞いたところ、1990年代後半まで使われていたようですが、今回の descrypt ではなく、もう少し複雑に改良したものが使われていたようです。
パスワード付きZIPファイルの解析(追記)
パスワード付きZIPファイルの解析方法を追記しておきます。
まず、パスワード付きZIPファイルの暗号方式は、大きく分けて 2つあります。1つは、普通にパスワード付きZIPファイルを作ると使われる ZipCrypto(Traditional PKWARE Encryption とも呼ばれる)で、もう 1つが AES を使用した方式です。
どちらの方式が使われているかは、以下のコマンドで確認できます。対象のパスワード付きZIPファイルは、setodaNote CTF Exhibition の Misc の strong_password の問題で提供されたファイルです。ZipCrypto Store と表示されています。ZipCrypto の方が使われていて、Store は圧縮されていないことを示します。
$ 7z l -slt TopSecret.zip
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=ja_JP.UTF-8,Utf16=on,HugeFiles=on,64 bits,2 CPUs Intel(R) Core(TM) i5-10210U CPU @ 1.60GHz (806EC),ASM,AES-NI)
Scanning the drive for archives:
1 file, 245 bytes (1 KiB)
Listing archive: TopSecret.zip
--
Path = TopSecret.zip
Type = zip
Physical Size = 245
----------
Path = TopSecret.txt
Folder = -
Size = 73
Packed Size = 85
Modified = 2021-07-14 18:21:58
Created = 2021-07-13 13:50:16
Accessed = 2021-07-14 18:21:59
Attributes = A
Encrypted = +
Comment =
CRC = 8578F5F9
Method = ZipCrypto Store
Host OS = FAT
Version = 20
Volume Index = 0
今回は、ZipCrypto の方を対象としてやっていきます。
パスワード付きZIPファイルに対して、John the Ripper に同梱されている zip2john を実行します。
出力結果のうち、:
の直後に注目します。今回は、$pkzip$
でした。
$ zip2john TopSecret.zip > TopSecret.zip.hash
ver 2.0 TopSecret.zip/TopSecret.txt PKZIP Encr: cmplen=85, decmplen=73, crc=8578F5F9 ts=92C0 cs=8578 type=0
$ cat TopSecret.zip.hash
TopSecret.zip/TopSecret.txt:$pkzip$1*1*2*0*55*49*8578f5f9*0*2b*0*55*8578*598c6b323287ee253bad1378d1f4fe4d91648ea9f1e60fe80374b917e47421b6f3379cf786e9c22453d02e8192c2aecd93122be13b2c348d02c0229c6c8a6b433ac24b17d0e18f215252601daafe595eb6ba28eab3*$/pkzip$:TopSecret.txt:TopSecret.zip::TopSecret.zip
以下のサイトを見て、一致するハッシュタイプを探します。$pkzip$
は PKZIP のバージョン1 らしく、以下の hashcat が対応しているハッシュ一覧を見ると、サポートされてなさそうです。
hashcat.net
今時、バージョン1 って、なんかおかしいと思ったので、最新の John the Ripper(Windows版)をダウンロードして、もう一度やってみました。すると、$pkzip2$ になってました。今回は、Store ということで圧縮されてない単体ファイルなので、17210 が該当すると思います。
$ run/zip2john.exe TopSecret.zip
ver 2.0 TopSecret.zip/TopSecret.txt PKZIP Encr: cmplen=85, decmplen=73, crc=8578F5F9
TopSecret.zip/TopSecret.txt:$pkzip2$1*1*2*0*55*49*8578f5f9*0*2b*0*55*8578*92c0*598c6b323287ee253bad1378d1f4fe4d91648ea9f1e60fe80374b917e47421b6f3379cf786e9c22453d02e8192c2aecd93122be13b2c348d02c0229c6c8a6b433ac24b17d0e18f215252601daafe595eb6ba28eab3*$/pkzip2$:TopSecret.txt:TopSecret.zip::TopSecret.zip
あとは、うまくマスクを作って実行すればよいです。今回の対象の問題(setodaNote CTF Exhibition の Misc の strong_password の問題)は、パスワードは 13文字で、以下のように構成されています。52*52*52*6*6
のパターンがあり、5,061,888 通りのようです。年月日の 2択が外れたら、2倍でしょうか。
- 案件コード:英文字 3文字(52×52×52)
- 記号:6種類(
@#$%!-
)のうちから 1文字(6)
- 年月日:数字 8桁(ZIPファイルの中身を見ると、20210713 か 20210714 だと思う)
- 記号:6種類(
@#$%!-
)のうちから 1文字(6)
英大文字、英小文字の組み込み文字セットはありませんので、カスタム文字セットを使います。-1 ?l?u
となります。次に、記号 6種類についてもカスタム文字セットを使います。-2 @#$%!-
となります。年月日については、いったん、20210713 でやってみて、どれぐらい時間で完了するのかを見てから振っていきたいと思います。
うーん、コマンドラインで記号を指定するのは難しいです。hashcat では、ファイルによる指定をサポートしています。カスタム文字セットと、指定するマスクをファイルに記述して実行することが出来ます。
例えば、デフォルトのカスタム文字セットと、デフォルトのマスクもファイルで実現されています。以下のように、カンマ区切りで、最初の 3つがデフォルトのカスタム文字セットで、最後が、デフォルトのマスクです。これを変更して作ってみました。
$ cat /usr/share/hashcat/masks/hashcat-default.hcmask
?l?d?u,?l?d,?l?d*!$@_,?1?2?2?2?2?2?2?3?3?3?3?d?d?d?d
$ cat org.hcmask
?l?u,@#$%!-,?l?d*!$@_,?1?1?1?220210713?2
では、早速実行してみます。うーん、残念ながらパスワードを見つけることが出来なかったようです。
$ hashcat -m 17210 -a 3 TopSecret.zip_Win.hash org.hcmask
hashcat (v6.2.6) starting
OpenCL API (OpenCL 3.0 PoCL 3.1+debian Linux, None+Asserts, RELOC, SPIR, LLVM 15.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform
==================================================================================================================================================
* Device
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Optimizers applied:
* Not-Iterated
* Single-Hash
* Single-Salt
* Brute-Force
Watchdog: Temperature abort trigger set to 90c
Host memory required for this attack: 0 MB
The wordlist or mask that you are using is too small.
This means that hashcat cannot use the full parallel power of your device(s).
Unless you supply more work, your cracking speed will drop.
For tips on supplying more work, see: https://hashcat.net/faq/morework
Approaching final keyspace - workload adjusted.
Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 17210 (PKZIP (Uncompressed))
Hash.Target......: $pkzip2$1*1*2*0*55*49*8578f5f9*0*2b*0*55*8578*92c0*...kzip2$
Time.Started.....: Sat Oct 26 17:41:04 2024 (3 secs)
Time.Estimated...: Sat Oct 26 17:41:07 2024 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?1?1?1?220210713?2 [13]
Guess.Charset....: -1 ?l?u, -2 @
Guess.Queue......: 1/1 (100.00%)
Speed.
Recovered........: 0/1 (0.00%) Digests (total), 0/1 (0.00%) Digests (new)
Progress.........: 5061888/5061888 (100.00%)
Rejected.........: 0/5061888 (0.00%)
Restore.Point....: 36/36 (100.00%)
Restore.Sub.
Candidate.Engine.: Device Generator
Candidates.
Hardware.Mon.
Started: Sat Oct 26 17:40:49 2024
Stopped: Sat Oct 26 17:41:08 2024
年月日のところを広げて、もう1回やってみます。出来ました!qYL%20210228!
でした!
$ cat org.hcmask
?l?u,@#$%!-,?l?d*!$@_,?1?1?1?22021?d?d?d?d?2
$ hashcat -m 17210 -a 3 TopSecret.zip_Win.hash org.hcmask
hashcat (v6.2.6) starting
OpenCL API (OpenCL 3.0 PoCL 3.1+debian Linux, None+Asserts, RELOC, SPIR, LLVM 15.0.6, SLEEF, DISTRO, POCL_DEBUG) - Platform
==================================================================================================================================================
* Device
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 1 digests; 1 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Optimizers applied:
* Not-Iterated
* Single-Hash
* Single-Salt
* Brute-Force
Watchdog: Temperature abort trigger set to 90c
Host memory required for this attack: 0 MB
Cracking performance lower than expected?
* Append -w 3 to the commandline.
This can cause your screen to lag.
* Append -S to the commandline.
This has a drastic speed impact but can be better for specific attacks.
Typical scenarios are a small wordlist but a large ruleset.
* Update your backend API runtime / driver the right way:
https://hashcat.net/faq/wrongdriver
* Create more work items to make use of your parallelization power:
https://hashcat.net/faq/morework
[s]tatus [p]ause [b]ypass [c]heckpoint [f]inish [q]uit => s
$pkzip2$1*1*2*0*55*49*8578f5f9*0*2b*0*55*8578*92c0*598c6b323287ee253bad1378d1f4fe4d91648ea9f1e60fe80374b917e47421b6f3379cf786e9c22453d02e8192c2aecd93122be13b2c348d02c0229c6c8a6b433ac24b17d0e18f215252601daafe595eb6ba28eab3*$/pkzip2$:qYL%20210228!
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 17210 (PKZIP (Uncompressed))
Hash.Target......: $pkzip2$1*1*2*0*55*49*8578f5f9*0*2b*0*55*8578*92c0*...kzip2$
Time.Started.....: Sat Oct 26 17:43:11 2024 (29 mins, 55 secs)
Time.Estimated...: Sat Oct 26 18:13:06 2024 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Mask.......: ?1?1?1?22021?d?d?d?d?2 [13]
Guess.Charset....: -1 ?l?u, -2 @
Guess.Queue......: 1/1 (100.00%)
Speed.
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 22246621184/50618880000 (43.95%)
Rejected.........: 0/22246621184 (0.00%)
Restore.Point....: 158208/360000 (43.95%)
Restore.Sub.
Candidate.Engine.: Device Generator
Candidates.
Hardware.Mon.
Started: Sat Oct 26 17:43:04 2024
Stopped: Sat Oct 26 18:13:09 2024
おわりに
今回は、hashcat の使い方と、実行時間の見積もりをやってみました。マスクを工夫すると、意外に短時間でパスワードは解読されてしまう気がしてきました。今後はもう少し複雑なパスワードを使いたいと思いました(笑)。
hashcat は、John the Ripper よりも使いやすいのと、どこかのサイトで、総当たりの場合は、スピードも1.5倍ぐらい出るとのことだったので、今後は hashcat をメインに使っていきたいと思います。
今回は、hashcat のロゴを使わせていただきました。ありがとうございます。
最後になりましたが、エンジニアグループのランキングに参加中です。
気楽にポチッとよろしくお願いいたします🙇
今回は以上です!
最後までお読みいただき、ありがとうございました。