Attack All Around

今やCTFと競プロばかりだが、トビタテ生のアメリカ留学やTOEFL奮闘記

Syskron Security CTF 2020 writeup

皆さん、こんにちは! Ken.Sです!

 

今回は、先程まで行われてたSyskron Security CTF 2020のwriteupです!最近のCTFはかなり難しいものが多いのですが、今回のCTFは問題数が多かったりtriviaという問題があったりで自身最高の6問解けました!(簡単なものしかないけど…)

 

ctf2020.syskron-security.com

 

 

結果

個人

解いた問題:6問

得点:395pt (20-25*3-100-200) ヒントで50pt使ったのでスコア上では345pt

 

m1z0r3

解いた問題:14問

得点:1500pt

順位:122th

 

Welcome letter

問題概要

分野:reading

得点:20pt (888 solves)

 

問題ファイル

  • welcome-letter.pdf

 

解法

pdf内上部にflagがある。

 

syskronCTF{th4nk-you}

 

 

Deadly malware

問題概要

分野:trivia (malware)

得点:25pt (645 solves)

 

問題文

Which malware targets safety systems of industrial networks and caused deaths due to explosion?

Flag format: syskronCTF{name-of-the-malware}

 

解法

初めて挑むtrivia問題です。とりあえず、問題文を訳してみます。

「産業ネットワークの安全システムを標的とし、爆発による死亡事故を起こしたマルウェアは?」

ということで、「マルウェア 爆発 産業ネットワーク」とこんな風にググってみると…

 

wired.jp

 

triton」っていうマルウェアが出てきました。爆発事故が起こったかどうかこの記事からは分かりませんが、調べてみると2009, 2010年にイラン国内の核燃料施設でウラン濃縮用遠心分離機を破壊したらしいです。wikiを見ても危なっかしい文章が並んでます。怖い。

 

syskronCTF{Triton}

 

 

Security framework

問題概要

分野:trivia (framework, standard)

得点:25pt (538 solves)

 

問題文

In 2018, the NIST published version 1.1 of their Cybersecurity Framework. What are the abbreviated names of the five core functions?

Flag format: syskronCTF{F1-F2-F3-F4-F5} (e.g., syskronCTF{AB-CD-EF-GH-IJ})

 

解法

「2018年、NISTは「Cybersecurity Framework」のバージョン1.1を発表しました。5つのコア機能の略称は?」

ということで、「Cybersecurity Framework version 1.1」とググります。そしたら以下のpdfを頂きました。

 

https://www.ipa.go.jp/files/000071204.pdf

 

p24 2.1 Framework Core で分かりやすい5個の略称が書いてあります。Flag formatからも問題ないでしょう。

 

syskronCTF{ID-PR-DE-RS-RC}

 

これって、5!通りのflagでも通るのでしょうか…。

 

 

Check digit

問題概要

分野:trivia (standard)

得点:25pt (487 solves)

 

問題文

We need a check digit in our keys. What do IMEI numbers, credit card numbers and UIC wagon numbers have in common?

Flag format: syskronCTF{relevant-ISO/IEC-standard} (e.g., syskronCTF{ISO/IEC-27001}).

 

解法

「鍵にチェックデジットが必要なんです IMEI番号、クレジットカード番号、UICワゴン番号の共通点は?」

 

知らない番号が二つ出てきました。

 

IMEI番号について (wikipediaより)

International Mobile Equipment Identity - Wikipedia

全ての携帯電話や一部の衛星電話に付与される識別番号。携帯電話のバッテリを外すと、そこに書いてあることが多い。また、*#06# と入力すれば携帯電話の画面にも表示できる。

 

UICワゴン番号について (wikipediaより)

客車のUIC分類記号 - Wikipedia

国際鉄道連合 (UIC) が開発した鉄道客車を分類する記号の国際的な体系について説明する。

 

まあ、なんかの識別番号って感じですね。結局クレジットカード番号を調べて規定が分かりました。

 

クレジットカードの番号 - Wikipedia

 

1行目にご丁寧に書いてあります。とりあえず、これをflag formatで囲って提出して通りました。出した後に気付いたんですが、ちゃんと他二つもこの規定に従っていました。IMEI番号は上記のwikipediaのところから「チェックディジットの計算」とこれもまた分かりやすく書いてありました。UIC番号は少し難しかったのですが、下のwikipediaから、check digitの計算方法がLuhn algorithmで行っていることが分かります。このアルゴリズムwikiに行くと同じ規定が出てきました。

 

UIC wagon numbers - Wikipedia

Luhn algorithm - Wikipedia

 

 

syskronCTF{ISO/IEC-7812}

 

 

Ladder password

問題概要

分野:plc-programming

得点:100pt (141 solves)

 

問題ファイル

  • 「A BB engineer encoded her password in ladder logic. Each "r" can be either 0 or 1 and represents a character in her password. "r1" is the first character, "r2" the second one, and so on. We assume that n1 is a small value and n3 is big. n2 may be between n1 and n3.

    Please decode the password to demonstrate that this technique of storing passwords is insecure.

    Flag format: r1r2r3…r9r10 (e.g., 1101…001).

    Be aware: BB deployed brute-force detection. You only have 3 attempts until lockout!

  • ladder-password.png

f:id:partender810:20201025154043p:plain

ladder-password.ong

解法

なんだこれ。そんな一言から始まりました。とりあえず、「ladder logic password」と調べましたが、良いサイトはヒットしません。そんなのありませんから…。

とりあえず、r1~10には0か1が入るみたいで1024通り考えられるのでbrute forceを防ぐために3回までしか試せないんですね。

 

与えられたpngファイルはラダー図と呼ばれるもので、回路設計の話です。詳しくは以下のサイトを…!

elec-tech.info

 

 

-||- みたいなのと、これに斜線が入ったもの、そして真ん中にRが入った3種類の接点が、黒と緑色に2種類あることが分かります。ではどういう風に考えたらいいかはwikipediaを参考にしました。

 

ラダー・ロジック - Wikipedia

 

基本は直列回路がANDで並列回路がORという感じでb1~b10がリテラル(0か1)となって、その結果がr1~r10に格納されるというイメージを持ちました。

-||(b1)--|\|(b2)-(r1) という場合は r1 = b1 and not b2, つまりb1=1かつb2=0ならr1=1, そうでないならr1=0という意味になります。

 

途中に出てくる値n1~3は問題文にあるようにn1<n2<n3という関係です。n2 is between n1 and n3. とありますが、これってn1 <= n2 <= n3という意味にもなるのでしょうか…。四角で囲われて上から順に[< n1, n2] とあれば n1 < n2 なら1, そうでないなら0となります。なので、後はb1~b10を2^10=1024通り全て試してr1~10の取り得る値を調べていこうと思いました。その結果答えが3通り以下に絞られるから3回まで試せるのかなと思っていました。

これを計算するプログラムを作る前に思い返したのですが、接点の種類は分かっても色の違いが分かりません。緑色がなんとなく蛍光色に見えたので、もしかして緑色は電気が通って黒は通らないのでは?とふと思いました。r1,r2で言うと

r1 = 黒 and 緑 and 緑 = 0, r2 = (n1 = n2は偽) and 緑 and 黒 = 0

といった感じです。r10まで求めると、0011100011となり提出したら通りました!

え、これで通ったの?と少し拍子抜けしましたが、まだ10 solves程度と時に解けたのは嬉しかったです!

 

後から調べてみたのですが、-|R|-は制御回路?らしきもので片方がtrueならもう片方はfalseになるような感じみたいです。間違ってたらごめんなさい…。また、「b1~b10を2^10=1024通り全て試してr1~10の取り得る値を調べ」ましたが26通りありました。この色の違いに気付かないといけないみたいですね。

 

0011100011

 

 

 

Leak audit

問題概要

分野:sql

得点:200pt (391 solves)

 

問題ファイル

  • We found an old dump of our employee database on the dark net! Please check the database and send us the requested information:

    1. How many employee records are in the file?
    2. Are there any employees that use the same password? (If true, send us the password for further investigation.)
    3. In 2017, we switched to bcrypt to securely store the passwords. How many records are protected with bcrypt?

    Flag format: answer1_answer2_answer3 (e.g., 1000_passw0rd_987).

  • Hint : Knowing SQL doesn't hurt.
  • BB-inDu57rY-P0W3R-L34k3r2.db

 

解法

SQLを知っている人はこの問題ササっと解けます。ヒントは役に立たないですね…SQL使わない解法は、まあ全部表示して手で数えるとかかな…。

 

1.How many employee records are in the file? (ファイルに何人の従業員が記載されていますか?)

table名を確認してCOUNTすればOKです。

 

sqlite> .tables
personal
sqlite> SELECT COUNT(*) FROM personal;
376

 

 

2.Are there any employees that use the same password? (If true, send us the password for further investigation.) (同じパスワードを使っている従業員はいますか?いたらそのパスワードを教えて)

まあ、flag formatからいるんでしょうね。

 

SQL初心者なのでググって調べました。

重複したデータを抽出する|プログラムメモ

 

sqlite> SELECT password FROM personal GROUP BY password HAVING COUNT(password) > 1;
mah6geiVoo

 

 

3.In 2017, we switched to bcrypt to securely store the passwords. How many records are protected with bcrypt? (2017年にbcryptでセキュアにパスワードを管理するようにしたけど、そうしている従業員は何人いる?)

 

bcryptってなんやねん、となったので調べてみました。

bcrypt - Wikipedia

 

簡単に言うと、パスワードをデータベース内に保存する際にハッシュを取って保存することで、仮にデータベース内を見られてもパスワードがバレることはないという感じです。

以下のクエリでそんなパスワードがあるか確認します。

 

sqlite> SELECT password FROM personal ORDER BY password;
$2b$10$/dd1tLbClIU85/pkthZvee9NvDvOYADy6/iSXGIEQxSVAhvRSASj6
$2b$10$1YdjEQ97.itIoDM3WQLBZORGQJuekq6OYFwaB6actkNzJZyU3ZFUy
$2b$10$5VRLne6dbC.qgBrnj9AFTeXpWn6Hkiv2UIUepqCChNOE2YmMCp5qm

 

ORDER BYでアルファベット順にしたら固まって出てきました。wikiにあるように$2b$という列から始まりました。

 

あとはそんな文字列から始まるpasswordをクエリでCOUNTします。

 

sqlite> SELECT COUNT(password) FROM personal WHERE password LIKE "$2b$%";
21

 

よって、flagは以下の通りです。

 

376_mah6geiVoo_21

 

 

 

 

ここまでは自力で解けました。以降は自力では解けなかったものを紹介します(チームで解けたり解けなかったり)

 

解けなかった問題

Vulnerable RTOS

問題概要

 分野:vulnerability

得点:25pt (708 solves)

 

問題文

What are the names of 11 zero-day vulnerabilities that affect a wide range of real-time operating systems?

Flag format: syskronCTF{name-of-the-vulnerability}

 

解法

 「広範囲のリアルタイムOSに影響を与える11のゼロデイ脆弱性の名前は?」とあるので、「11 zeroday」とググったら以下のサイトを発見! しかし、脆弱性の名前などは出てこず…

FireEye、2013年に11件のゼロデイ攻撃を検出 | FireEye

 

チームメイトが「11個 ゼロデイ リアルタイムOS」とググったら一発で正解しました…。悔しい。

monoist.atmarkit.co.jp

 

syskronCTF{URGENT/11} 

 

 

DoS Attack

問題概要

分野:packet-analysis

得点:100pt (340 solves)

 

問題ファイル
  • 「One customer of Senork Vertriebs GmbH reports that some older Siemens devices repeatedly crash. We looked into it and it seems that there is some malicious network traffic that triggers a DoS condition. Can you please identify the malware used in the DoS attack? We attached the relevant network traffic.

    Flag format: syskronCTF{name-of-the-malware}」 

  • dos-attack.pcap

 

 

解法

パケットを見て何のマルウェアか特定する問題です。

パケット調べると毎回60バイトでDNSプロトコルで1024回送っています。DNSを介してのDDoS攻撃だからDNSリフレクター攻撃かと思ったが、マルウェアを特定できない…。 DNSクエリなのにドメイン名が無いということも教えてもらいました(こういった基礎知識の無さが滲み出た)。

結果として、ヒントを使いました。

 

They bought some older SIPROTEC 4 protection relays.

 

とりあえず、「SIPROTEC」でググると「siemens」というドイツの企業が出てきました。私はここから動けなかったのですが、チームメイト曰く「SIPROTEC siemens malware」とググったら「CVE-2015-5374」という脆弱性が出てきて、「malware CVE-2015-5374」でググったら以下のサイトが出たとのことです。

eset-info.canon-its.jp

 

 

syskronCTF{Industroyer}

 

Security advisory

問題概要

分野:crypto

得点:400pt (16solves)

この問題はチームでも解けず…

 

問題ファイル
  • Senork published another security advisory. You have to review it (but maybe, there is some hidden information).

    https://www.senork.de/files/BSA-2020-11.pdf

  •  

    Hint : Sometimes, metadata exposes sensitive information. 

 

解法

最初はpdfだけ与えられて全く分からず…。ヒントを使ってpdfのメタデータに何かあるのだろうと見てみました。Acrobatで開いてファイル→プロパティで見れます。

 

f:id:partender810:20201025192342p:plain

メタデータ

 

アプリケーションが変な16進数ですね。とりあえず、long_to_bytesしてみました。

 

b'damn metadata; here you go: 2ZdkKTtprGGddHh33' 

 

 

うおお!文字になった!

けど、2Z~33をsyskronCTF{}で囲ってもIncorrectでした…。まあこれじゃcrypto要素ゼロですもんね。pdfはBlowfish暗号について書いているので調べてみました。Leak auditでのbcryptと同じ方式らしいです。そして、pdf内の「Technical description of the vulnerabilities」に16進数12桁が8個ありました。Blowfish暗号はブロック暗号だからこれが暗号文か!?とか思いましたが、復号できず…。OFBモードとか書いてあるなとか思いつつ、ここでタイムアップでした。

 

writeup見つけたら更新しますね。見つけたので紹介します。

 

ctftime.org

 

足りなかったものは、メタデータのアプリケーションで得られた2Z~33の文字列が16bytesだったということに気付くところと、Cyberchefと友達になれなかったところです。

2Z~33の文字列をhexに直して前半16桁をkey, 後半をivとします。あとはpdf内の16進数を全て繋げたものを暗号文として、Blowfishのdecryptを行うとflagが手に入ります。

 

7,8割は出来てたのになぁ…悔しい!!!

 

syskronCTF{you-Just-anaLyzed-your-1st-advisorY}

 

 

 

 

 

問題ファイルはGitHubに載せてます。solverは今回無いので…

github.com

 

6回もcorrect!と出ると非常に気持ちが良い!Ladder passwordは得点の割に難しくてsolves数も少なかったし、その分解けたというか予想が当たった時は嬉しかったですね!

 

それでは!

 

See you next time!

SECCON CTF 2020 This is RSA writeup

こんにちは!Ken.Sです!

 

昨日まで開催されていたSECCON CTF 2020のwriteupを書いていこうと思います!

開催期間中には解法が一切分からず、チームメイトからヒント(ほぼ答え)を貰ってなんとかフラグを出せました…。実力じゃあないです(笑)

 

 

score.lmt.seccon.jp

 

 

 

 

問題概要

問題名:This is RSA

分野:crypto (warmup)

得点:124pt (62 solves)

 

 

問題ファイル

  • rsa.rb
  • output.txt

 

f:id:partender810:20201012120128p:plain

rsa.rb

 

解法

rsa.rbを見るとp,qをget_primeという関数で生成しており、その後はいつも通りの暗号化を行っています。このget_primeに何かあるのだろうとにらめっこしてたら時間切れ…。rubyの仕様調べたのに、結局何も分かりませんでした。

 

肝となるのはunpack1のところ。これ、一文字ずつASCIIコードに直してたんですね。つまりget_primeの流れとして

  1. 512bitsの乱数を生成する。
  2. 一桁ずつ上からASCIIコードに直す(16進数)。
  3. その16進数が素数だったらその値を返す。そうでないなら1. へ戻る。

 

という感じです。2. が伝わりにくそうなので仮に1234を1.で生成したとすると、

0x31323334という数字を作ります。(1->0x31, 2->0x32, 3->0x33, 4->0x44)

ASCII文字コードの数字は0x30~0x39なので、p,qを16進数に直すと偶数桁目に必ず3が出てきます。これが分かれば解けてたなぁ。

 

0x3*3*3*3*...3*3*3* × 0x3*3*3*3*...3*3*3* = N となります(* : 0~9)。*に0~9を当てはめていくbruteforceをします。全通り調べると10^64くらいになります(512bitsを16進数にすると128桁、その内不明なのは半分)。普通にbruteforceすることはできません。

 

ではここでかけ算の筆算を利用します。例えば、123456×987654の下二桁を求めてください。この際、必要な計算は掛けられる数掛ける数の下二桁だけ計算すればいいです。つまり、56×54の下二桁です。実際に計算してみると分かります。123456 × 987654 = 121931812224, 56 × 54 = 3024。下二桁は一致しました。では、下四桁を求めるにはどうしたらいいでしょうか。3456 × 7654の下四桁と同じです。この性質は筆算をしてみれば分かります。

 

f:id:partender810:20201012122748p:plain

筆算するとこんな感じ

 

p,qを求めるにはその積であるNと先の性質を利用します。pの下二桁とqの下二桁を掛けるとNの下二桁と一致します。これは16進数にしても同じ話です。p,qの下二桁は3*と分かっているので10×10通りで求められます。下二桁を求めたら下四桁を求めます。下二桁3m×3nが一致したとすると今度は3*3m × 3*3n = Nの下四桁を求めればいいのです。これを繰り返すと、128×10×10程度で求まります。注意すべき点としてはNを16進数に変換して行ってください。

 

p,qが求まったらあとはいつも通りに復号すればOKです。

 

 solverや問題ファイルはこのgithubに載せてます。

github.com

 

 

最近、rubyで書かれた問題が多くなりました。前回出た時は特に気にしなくてよかったですが、今回は細かい点を突かれました。rubyの勉強もしないと…!

 

 

それでは!

 

 

See you next time!

 

勉強とボウリングを両立させてた昔話

皆さん、こんにちは! Ken.Sです!

 

少し前に関東学生ボウリング連盟OBの先輩が、学生時代の話をtwitterに載せていました。その方は、ジュニアの頃からボウリングをしていて東大に現役合格、卒業後ナショナルチームに在籍する等文武両道という言葉がすごく似合う私の尊敬している方の一人です。その方のツイートがすごい反響していて、学歴やボウリングの腕などはその方に劣りますがニーズがあればいいな、ということでどう勉強していたかなど自分の昔話を書いていこうと思います(二番煎じとか言わないで)。

 

この話を書くもう一つの理由として、早稲田大学に進学したいと思うジュニアボウラーが少しでも多くなればいいなという思いもあります。学部生時代、早稲田に入りたいけど勉強はしたくない、できないといったお声を何回か頂きました。確かに簡単に入れる大学ではないかもしれません。しかし、ボウリングを続けながらも入れるということを皆さんにお伝えできればいいなと思い、ブログにまとめさせてもらいます。私も中学の頃には落ちこぼれを経験しました。しかし私は成長した時には環境の変化がありました。これが読んでくださってる方へ良いアドバイスになればいいなと思っています。

 

特にジュニアボウラーの方や、お子さんがボウリングをしているという親御さんに読んでいただけたら幸いです。

 

 

 

小学生時代

勉強面

低学年の頃は、授業中積極的に発言し問題を答える優秀な生徒だったと思います。好奇心旺盛だったので、それが活きたのかなと思います。クラスの何人かは塾に通っていたみたいですが、私はこの頃はまだ通っていなかったです。「塾に行ってないのに、この問題解けるのすごい!」と褒められたのが嬉しかったのを今でも覚えています。その頃自分は頭が良いものだと思っていました。

 

高学年になると、集団指導と個別指導の塾二つに通い始め、5年生くらいからは個別指導(明光義塾)一つにしました。しかしそこで受けた小学生模擬試験で洗礼を受けます。偏差値60はいくだろ、とか思ってました(その難しさすら知らない子供だったなぁ)。しかし結果は50すらいかない平均以下。文系科目に至っては40もいってなかった記憶があります。気付けば小学校のクラスでもこの人は自分より上だ、と思う生徒がどんどん増え、落ち込むこともありました。自尊心が高かったなぁ。

 

ただ、ボウリング部のある青稜中学に入りたいと思うようになってからは、あまり周りの事は気にならなくなった感じです。青稜に行ければいいや、って感じです。行ってた塾が中学生や高校生もいたので、その人達と話すのも楽しいということで塾に結構入り浸ってました。塾に行きだしたのが一つの環境の変化でした。人生で一番勉強したのはこの時期だと思います(今もっとやれ)。しかし自主的に勉強したということはあまりなく、学校や塾の宿題をこなすのがほとんどでした。放課後~塾間も多少遊んでいましたし、塾後も宿題が終わればテレビを見てました。印象に残っている塾での勉強法は、解けなかった問題を講師の方ともう一回解く復習です。定期的に模試があって、解けない問題も多かったので良い勉強法だったなと思います。

 

中学受験は高校受験や大学受験と違い、複数回受験できます。青稜は確か最大5回受けられました。受験が近くなってもいいとこ50%かなくらいの成績でしたが、2回目の受験で合格しました。

 

この時に大事だったなと思うことは、自分はできるもんだと思い込んでたことです。確かに小3くらいまではテストは大体満点で理解できないということは無かったと思います(大体の子がそうでした)。低学年での一番の壁は九九と言われていますが、私は天才バカボンのソフト?があり、歌で覚えたり、九九の問題をPCで回答するのをやっていたのであまり苦にしなかったです。しかし、学年が上がるにつれ周りとの差を感じました。ちょっと前は出来てたというのと、勉強ができないとは思われたくないという見栄で勉強してました。今思うと、小さい見栄ですがそれが活力になったのは良かったと思います。そしてこの見栄が後にも活きています。一番仲良かった友達が「俺は絶対一橋大学に入る」と言ってて実現したかは分かりませんが、一緒に勉強を頑張る友達がいたというのも大きかったです。

 

ボウリング面

地元のボウリング場のスクールが小3から入れるということで、それを知った小1から2年間待ちに待ってそのスクールに入りました。知識面や技術など教わりましたが、基本は投げて遊ぶといった環境でした。中古ボールを頂いてボールを作り、小6の最後に初めて200点いったのを覚えています。けど今は小学生でも2000/9Gとか出してる子がいてすごいなぁと思います。この頃は、狙うスパットはいつでも同じところだしレーン変化なんて知らなかったです。今の子よりかは全然知らないと思います。

 

ボウリングを続けた大きな要因は、やってて楽しかったことです。スクールも少し上のお兄さんお姉さんに可愛がってもらい、祖母の家の近くにある六甲ボウルの方にも大変よくしてもらいました。大学生になって新人戦で六甲行った時は嬉しかったなあ。

 

中学生時代

勉強面

中学で勉強に対する環境と意識の変化がありましたので、ここは長くなります。

 

中1の頃は、定期試験に対して勉強するということを全くしてませんでした。得意な理系科目や、小6の最後に塾で習った英語はなんとかなってました(平均点はギリクリアするくらい)。しかし文系科目は一切できず、地理は20点台を二回出しました。一年前の中学受験ではあれだけ勉強したのに全く勉強せず、何をしていたのかすら覚えてません…。50位/250人程だった順位も下から数えた方が早いくらいまで落ちこぼれました。成績が悪い生徒が強制的に受けさせられる基礎講習に引っかかるくらいになりました。

 

青稜は中2になると、一つのクラスに成績優秀者を集めた優クラス(約40人)を作ります。当然そのクラスに入れるわけもなく、一般クラスに割り振られます。その頃から、定期試験で良い成績(クラスで何位、学年何位)を取ると小遣いボーナスが支給される制度が我が家でできました。

定期試験前は部活は無かったので、中2以降基本的に部屋に籠って対策してました。中3くらいから近くのボウリング場に行って練習して、その後勉強してました。定期試験前でなくても、試合でない限り勉強しない日はあまり作ってなかったです。1~2時間宿題か分からない範囲を潰して、授業中に話が一切分からないということは避けました。反省として、文系科目はもっと授業の話を聞いて「流れ」を理解するようにすればよかったなと思っています。勉強方法としては、最初30分はYouTube流しながらやって、机に向かう理由を作りました。

結果として中2後半の定期試験で、学年7位に入る結果が出ました!小遣いという小さいインセンティブが、大きな成長に繋がりました。一回良い成績を取ると、まぐれだと思われたくないのでまた頑張り、中3の頃には優クラスに入るまでになりました。ここの環境の変化が自分は一番大きいと思っています。

 

中2の時、実はあまり優クラスに入りたくないなと思ってました(今だから言えることかも)。レベル高い中に入って劣等感を感じたくないという小さい見栄です。それだったら一般クラスでクラス一位取って満足してたいとか思ってました。しかし実際に優クラスに入ると、落ちたくないので頑張ります。そして、クラスメイトみんな勉強ができるので、質問したり教えてもらったり勉強するには最適な環境でした。また、そんなクラスなのでクラス平均点は異常に高かったです。得意科目も苦手科目もそこが良い目標になっていました。得意科目では平均点以下は取りたくないし、苦手科目なら平均点越えれば自身になります。この良い点数取りたいという見栄と優クラスでありたいというプライドが作るプレッシャーが、勉強するモチベーションになってました。優クラスに入ってよかったなと本当に思います。

 

この期間は、落ちこぼれから優クラスまで幅広い経験をしました。環境の違いを多く感じ、それがまた活きたのかなと思います。

 

ボウリング面

中1で都大会5位になって初めてそしてそこから3年間全国大会に出ました。が、これといった成績は無く、中3最後に関東3位になったくらいです(ボウラー人口は少ない)。

ボールの違いやレーン変化もまだ分からず、中3最後の都大会で遅くなったレーンに対応できず4の字したのを覚えています。最後に思いきり内に寄ったらよかったけどその景色が初めてで対応が難しかった記憶があります。曲がるボールが良いという勘違いもあり、多少のコントロールとハマれば打てる(けど長続きしない)程度のボウラーでした。

しかし、中3の途中から支部リーグに参加したことにより第一次成長期に入ります。経験豊富な方が多くいたので、基本的なレーン対応や1ゲーム勝負の流れ運びなどを学ばせてもらいました。私が一番大きい学びと思うのは、負けず嫌いな方が多くいたので本気で勝負する楽しさと負けた悔しさを知ったことです。そんな本気の勝負を毎週できたのは、とても大きい経験になりました。ビアがかかったフレームになるたびに皆さんが集まってきたのは懐かしいなぁ。

 

 

高校生時代

勉強面

高2から文理でクラスが分かれましたが、なんとか理系優クラスを維持しました。高校生になると模試が多くなり、定期試験対策に加えて幅広く勉強しなければなりません。定期試験対策で成り上がった身なので、模試対策もちゃんとやらねばなりません。定期試験前では青チャートや重要問題集、単語帳などをやってました。休みの日にはセンター試験を解いたりなど、基本的には勉強に打ち込んでました。高2まではこの大学に行きたい、とかはあまりなく名の知れたところに行ければいいなくらいに思っていたのですが、高3になって定期試験などの学校の成績や部活動や生徒会への取り組みなどが評価される「指定校推薦」があることを知りました(遅い)。やっとどういう大学に行くかを考えるようになって、情報系の勉強がしたいなと思い早稲田大学へ行くことを決意しました。評定などの規準はクリアしていたのでその指定校推薦を利用しました。普段の勉強に加え志望理由書や面接練習などもしてました。

面接当日は最初の説明会から面接までの間、受験生が多かったこともあり結構長い時間待たされました。面接は緊張して上手く話せず、動揺して帰りの電車で逆方向に乗ってしまったりなどありましたが、なんとか合格しました。赤羽のナショナルトレーニングセンターにいる時に結果をもらったのが懐かしいです。

 

ボウリング面

定期試験前以外には基本的に毎日田町ハイレーンで練習してました。高1の高校対抗で結果を残すようになってからより一層練習するようになり、田町ハイレーン様のご厚意により様々なコンディションで練習させていただきました。ウッドとアーマーの二種類のレーンがあるセンターなんてもう無いんじゃないかな…。スタッフの方には本当によくしていただき、そんな環境があったからこそボウリングを続けられたと思います。

 

大学生時代

勉強面

正直、勉強はあんまりしてませんでした。両立はしてませんでしたね。テスト前に詰め込んで普通の成績を取るということを繰り返してました。ただプログラミングの授業だけは意欲的に受けてました(javaは分からなかった…)。学年が上がるにつれてだんだん難しくなるのに他の人は余裕そうにしているので焦りもありつつ、最初からちゃんとやっとけと定期的に反省してました(今もそう)。

就職は全く考えてなく、大学院の推薦を何とか取れたので修士課程に進学することを3年の終わりに確定させました。4年次に配属された研究室の先輩や同期がみんなすごかったので、ちゃんと勉強や研究するようになりました。これも環境の変化がもたらした大きな成長です。これらが今に繋がってます。例えば、日本からアメリカへ環境を変えることで成長できると思い留学を決意しました。変化を好むことができる人になりたいんなぁ。

 

ボウリング面

高校でできなかった全国タイトルを取ることに努力してました。中でも4年次の後半は一番悩んだけど自分の全盛期だったなと思います。大学時のボウリングの話をするととても長くなるので割愛しますが、自分の第二次成長期はこの時期で特に佐取プロに教わったことが大きいです。これも環境の変化かなと思います。今思うと一緒にジャパンオープンに出させてもらったことがこれだけの良い変化をもたらしたのかと、出会いってすごいなと思います。欠員が出たから代打お願いLINEに即答して良かったです。

 

 

私は漫画暗殺教室の「第二の刃」という言葉が大好きです。落ちこぼれのE組生徒が殺せんせーを暗殺できればいいから勉強しない、と言うとそれに殺せんせーが怒るというシーンです。優秀な暗殺者はメインの計画は上手くいかない方が多いからサブプランをいくつも用意する、などというエピソードから私も勉強とボウリング両方やっていて良かったと思います。特にジュニアボウラーの方にはボウリングはもちろんのこと、ボウリングだけでいいとはならずに他の分野(勉強に限らず)も取り組んでみてほしいと思います。ボウリングを行っている方は多いですがもう一つの分野も続けているってかなり珍しく個性が出ます。自分という一人の人間を形成するにはそういうことが重要なんだなと、就活を通じてですが感じました。

 

 

長くなりましたが、自分の人生を振り返ってみました。これがジュニアボウラーの方に少しでも刺さってくれたらいいなと思います。そして、早稲田に入学したいと思ってくれたらいいなと思います。

 

 

それでは!

 

See you next time!

TokyoWesterns CTF 6th 2020 一問も解けなかった話

皆さん、こんにちは! Ken.Sです!

 

今日の朝9時まで開催されてたTokyoWesterns CTF 6th 2020に参加しました! 結果はタイトルの通り、一問も解けませんでした…。一問あと一歩のとこまで行けたので、その問題のwriteupを書いていこうと思います。

 

score.ctf.westerns.tokyo

 

 

問題概要

問題名:twin-d

分野:crypto

得点:172pt (68 solves)

 

問題ファイル

  • task.rb
  • output

f:id:partender810:20200921122902p:plain

task.rb

 

解法

まずは、問題ファイルから見ていきましょう。rubyプログラムは久しぶりですが、特段変わったことはしてません。流れはこんな感じです。

 

p,q <- 1024bitsの素数
phi_n = (p-1)*(q-1) とする
while:
d <- 1024bitsの素数
gcd(phi_n,d) == 1 and gcd(phi_n,d+2) == 1 ならbreak
e1 = d^-1 mod phi_n
e2 = (d+2)^-1 mod phi_n
//e1*d = 1 mod phi_n
//e2*(d+2) = 1 mod phi_n
msg <- flag.txt
enc = pow(msg,e1,n)
p*q,e1,e2,encを出力

 

phi_nについて秘密鍵dの逆数e1と、d+2の逆数e2が与えられており、出力された結果がoutputに入っています。

 

k,k'を正整数とすると、

e1*d = k*phi_n+1 ・・・①

e2*(d+2) = k'*phi_n+1 ・・・②

となります。

 

②をdの式にすると、

d+2 = (k'*phi_n+1)/e2

d = (k'*phi_n+1)/e2 - 2 ・・・③

となります。

 

③を①に代入すると、

e1*( (k'*phi_n+1)/e2 - 2 ) = k*phi_n+1

phi_nの式に変形します。まず、両辺にe2を掛け、

e1*(k'*phi_n+1-2*e2) = e2*k*phi_n+e2

e1*k'*phi_n + e1 - 2*e1*e2 = e2*k*phi_n+e2

e1*k'*phi_n - e2*k*phi_n = e2 + 2*e1*e2 - e1

phi_n*(e1*k' - e2*k) = e2 + 2*e1*e2 - e1

となり、

e2 + 2*e1*e2 - e1 = 0 mod phi_n ・・・⑤

ということが分かりました。

 

ここまでは導出できました!!

 

ここから、左辺を素因数分解してphi_nの候補を出してやろうと思いましたが、数がデカすぎて無理でした…。ここで、他の方法が思い浮かばず断念しました。

 

では、続きを。

 

式⑤を変形すると、

2*e1*e2 = e1-e2 mod phi_n

となります。

ここから、同じ平文を違う公開鍵で暗号化した暗号文を導出します。

 

enc = m^e1 mod n

enc^(2*e2) = m^(e1*2*e2) mod n = m^(e1-e2) mod n

 

新しい暗号文を手に入れることができました。

gcd(e1,e1-e2) = 1より、common modulus attackが可能です。やり方はこの記事を見てみてください。

 

partender810.hatenablog.com

 

new_enc = enc^(2*e2) mod n とすると

e1*s1+(e1-e2)*s2 = 1となるs1,s2を求めればOKです。

 

TWCTF{even_if_it_is_f4+e}

 

 

最後のcommon modulus attackへの導き方はこちらを参考にさせていただきました。

 

qiita.com

 

この方、sqrtも解かれててすごい…!自分も挑戦はしてみましたが、一切歯が立たず…。最近のCTFはかなり難しくなっている気がします。

 

github.com

 

それでは!

 

See you next time!

InterKosenCTF 2020 ciphertexts writeup

皆さん、こんにちは! Ken.Sです!

 

丁度本日9/6の22時に終わったInterKosenCTFの問題の一つのciphertextsのwriteupを書いていきます!

InterKosenCTF

score.kosenctf.com

 

 

問題概要

問題名:ciphertexts

分野:crypto warmup

得点:201pt (33 solves)

 

 

問題文

Since there are two ciphertexts, it is easy to solve, isn't it?

 

問題ファイル

  • main.py
  • output.txt

github.com

(writeupも掲載しています。)

 

f:id:partender810:20200906221952p:plain

main.py

 

解法

本番ではtar.gzファイルで渡されて、解凍してみると上の二つのファイルが手に入ります。

main.pyを実行したものがoutput.txtに出力されています。

main.pyについて、512bitの素数p,q,rの内、n1はp,qの積、n2はp,q,rの積となっています。次に、20bitの素数e1とそのe1の次に小さい素数e2を格納し、それらを公開鍵としてbytes_to_longしたflagを基に暗号文c1,c2を形成します。最後にn,e,cを出力します。(某企業名みたい)

 

n2がq,rの積だったら、n1とn2の最小公倍数からqを求めて、そのqでn1,n2を割ればp,rは得られるなぁと思いつつ、p,qを求める方法を探しましたが見つからず断念。同じ平文を暗号化しているのでここになにかヒントがあるのだろうと、いつものslideshareを閲覧。

www.slideshare.net

 

その8にあるcommon modulus attackがそれっぽい!(最初はその次のページの方法かなとも思ったけど、e個も暗号文を与えられてないので諦め)

 

けど、同一のnを用いて暗号文が形成されている必要があるので、ここを上手くやらないといけません。つまり、

c'1 = m^e1 mod n2    or     c'2 = m^e2 mod n1

を求める必要があります。

n2 = r * n1 なので、なんか行けそうな気がするなぁと考えてたらやはりいけました。

 

c2 = m^e2 mod n2より

m^e2 = k*n2 + c2 (kは整数) = k*r*n1 + c2 = c2 mod n1

∴ c'2 = c2 mod n1 と簡単に求まりました。

 

 

 

では、この求めたc'2やc1,e1,e2,n1を使ってcommon modulus attackを行っていきます。

common modulus attackはこのサイトが分かりやすかったですが、ここでも噛み砕いて説明します。

elliptic-shiho.hatenablog.com

 

拡張ユークリッドの互除法を用います。簡単に言うと、

a*x + b*y = c (a,b,c : 既知)

となる、x,yの解の一つを求めるやつです。

mathtrain.jp

このサイトの「一次不定方程式への応用」ってところを読むと分かるはずです。

 

e1*s1 + e2*s2 = 1 となるs1,s2を求めます。そのs1,s2を使うと、

c1^s1*c'2^s2

= (m^e1)^s1*(m^e2)*s2 mod n1 = m^(e1*s1+e2*s2) mod n1 = m^1 = m

と平文mが求まります!

 

では、s1,s2を拡張ユークリッドの互除法でどうプログラムしていくかというと、このサイトを活用(丸パクリ)します。

tech.camph.net

 

 

こんな感じで平文mを求めてlong_to_bytesするとflagが出ました。

 

KosenCTF{HALDYN_D0M3}

 

solverも先のgithubに掲載しています。

 

 

このInterKosenCTFは昨日10時から始まったのですが、気付かず今日の夕方にやり始めてなんとかこの問題は解けたけど結局他の問題は解けなかったです。多分時間かけても変わらない結果だったと思います(笑)

 

それでは!

 

See you next time!

 

 

 

罰ゲーム ~愛の不時着3話視聴~

皆さん、こんにちは! Ken.Sです!

 

さて、皆さん8/9のインスタライブ見ていただけましたでしょうか。トビタテ生とコラボしたのですが、その中で「外国語やカタカナ」禁止ゲームをやって見事に負けました(笑) その罰ゲームとしまして、韓流ドラマ「愛の不時着」を見てそのレポート500字をまとめろとなりました…。3話まで見たので気の乗る方読んでください。

 

先に言っておきます。愛の不時着好きの方からは大変面白くないレポートになっています!

 

※以降、ネタバレ注意!

 

 

 

 

 

3話までのあらすじ

すごい財閥の娘セリは、ファッションブランドを立ち上げるほどの経営者として優秀だった。末娘であるにも関わらず、父親の後継者として二人の兄貴を差し置いて選ばれる。選ばれた翌日(?)に自身の会社のパラグライダースーツのテストで空に飛び立つ。無事、事故に遭う。発生した竜巻に吹き飛ばされ北朝鮮に不時着しそこで軍人に会う。まあこの二人がきっと結ばれるのでしょうが、軍人としては逮捕しないといけないので、まずここでセリは逃げます。軍人のミスやセリの幸運(?)もあり何とか韓国まで戻れた、と思ったらリの家付近で結局匿ってもらうことになりました。リをよく思わない上司、というか違法なことをしまくってる上司から目を付けられセリを匿っていることがバレ、セリを婚約者ということとします。そうなると当然本物のリの婚約者が現れるわけで、おそらく4話以降でかき回す存在になるのでしょう。他にもセリをよく思わない兄貴二人や、その片方と繋がっている詐欺師クや、そのクと密談しているリの上司など障害がいっぱい出てくるのでしょう。3話の最後は、船で違法出国しようとするが政府(?)に見つかり停止させられます。船の運転手が上手くはぐらかそうとするが結局二人が見つかるのですが、その瞬間キスして終わります。

 

 

3話までのレポート(感想)

疑問点というかあり得ないことが多すぎる!!

  • なぜ末娘を後継者に選ぶのか。いくら兄貴が無能だからといってそれはないだろう。(1話)
  • なぜ危険なパラグライダースーツのテストを社長自ら行うのか。(1話)
  • ファッションブランドがなぜパラグライダースーツを開発しているのか。(1話)
  • なぜ竜巻が起こることを予測できなかったのか。警報が出ているはずだ。(1話)
  • 竜巻に巻き込まれて生きているのが不思議。(1話)
  • 地雷一帯を駆け抜けて無事であるセリ(1話)
  • セリを見つけた途端なぜ拘束しないほど甘い奴が中隊長になっているのか。(1話)
  • ミスしたら死刑どころか末代までの恥、となるくらい軍人は厳しいものなのに勤務中に酒飲んだり韓流ドラマ見てセリを見逃すなんてことがあるのか。(1話)
  • 北朝鮮で韓流ドラマを見ても良いのか(1話)
  • 宿泊検査の時にキムチ倉に隠れている奴が婚約者なわけ無かろう(2話)
  • 北朝鮮の軍人に韓国製品を売ったらその時点で軍人に殺されるはずでは?(2話)
  • 指向性マイクがあんなに性能良いわけない(3話)
  • キスであの修羅場を乗り切れるわけ(3話)

と、疑いまくりの眼差しで視聴してました(笑) ただ、パラグライダー関連のことは2話でなんとなく明かされました。安楽死を望むほど精神的に弱ってたセリが、スイスで死のうとしてました。安楽死センター(?)の人にスイスを観光したら7割の人はそのまま家に帰るからという理由で、雪山の絶景を見た時に飛んでいるパラグライダーに感動したという経緯からパラグライダーが好きなのだろう。そしてそこにリもいたことから結ばれる伏線バリバリあるなぁと思いました。他の疑問は見続けたら解決されるのでしょうか

あと初めて知ったこととして、地雷は踏んだ瞬間に爆発するのではなく踏んで足を話したら爆発するのかと1話の描写から分かりました。

 

さてこれから、二人はまぁ結ばれるだろうから二人で韓国に行くのか北朝鮮に残るのかが気になります。その場合財閥やファッションブランド会社はどうなるのでしょうか…。それとも二人は離れ離れに…? でも女性陣があれだけ盛り上がっていることからそんなバッドエンディングではないでしょう(笑) 1話が1時間以上もありかつ全16話…。残りを見るかは気分次第です(笑)

 

 

と、懐疑心たっぷりでお届けした愛の不時着ですが、韓流ドラマにハマる方の気持ちは分かったつもりです。ここがすごい、というポイントは分からないのですが日本のものと遜色ないなと思っています。演技は上手いしセットもキレイで映像に関する疑問点は一切なかったです。あとは話が今後どうなるのか…。話の内容に関して少し否定的でありましたが、新しい分野を開拓できたのは良かったと思ってます!それに、話の続きも気になっているので、たまに続きを見ようかなと思います。

 

それでは!

 

See you next time!

 

 

 

knight tour problem を解いてみた

皆さん、こんにちは! Ken.Sです!

 

 

毎週トビタテ生とオンライン飲み会をしているのですが、そこでオセロや将棋などボードゲームの話になりました。インドに留学していた友達が、将棋をインド人に教えた話を聞かせてくれました。そのインド人はチェスは分かるようなのでそれを基に教えたそうなのですが、桂馬をknightのように動かしたそうなのです。友達は桂馬はknightの1/4しか動かせない、前2マスと横1マスしか行けないとちゃんと伝えたそうなのですがそのインド人に噓つき呼ばわりされてしまったそうです(笑) たしかにknightって特殊な駒で、子供のころに教えられて桂馬より動く幅が4倍になる!とかなり印象的だったことを今でも覚えています。

 

 

f:id:partender810:20200812121408p:plain

knightだけはqueenで代替できない駒

 

そんなknightに関する大学の課題で記憶に残っているのが「knight tour problem」ナイト・ツアー - Wikipedia です。

 

 

 

8×8のチェス盤においてknightが全てのマスを一回だけ訪れることは可能か、という問題です。解けることは知られており、それを示せというのが2年前にあった課題でした。この問題の事をそのトビタテ生に話したときに、「どのマスから始めても可能なのか」と質問してもらったのですが、その課題のレポートには角から始めた結果しかありませんでした。気になって調べた結果を書いていきますので、興味のある方是非読んでください! 反論はいくらでも受け付けます(笑)

 

 

まず初めに、c++で書いてみました。競プロでやはりc++の速さが強いので、pythonやgoに比べると書きにくさは感じるのですが勉強ということでc++を利用しました。深さ優先探索で実行しどこにも行けなかったらfalseを返し、1通りでも見つけたらtrueを返して終了するというプログラムを書いたのですが、c++の速さをもってしてもなかなか終わらない…。明らかに1周できないと判断できる場合(あるマスから行けるマスが全て既に訪れていてそのマスに行けない等)にfalseを返していなく、訪れたマスからどこにも行けなくなるまで計算を続けているので範囲の絞りが甘いのもあるのですが、丸2日かかっても終わりません…。総計算量を考えると、あるマスからチェス盤からはみ出さず行くことができるマス数の平均は5.25マス。その内の1マスはそのマスへ訪れる前に訪れているのでその分を引く。これを64回繰り返すので概算で(5.25-1)^64≒10^40となります。c++が1秒で100万通り計算できたとしても、約10^29日かかってしまう…。実際はもっと小さいですが、終わらないわけです。プログラム内にバグがありそれで遅くなっている可能性もありますが、直しても時間がかかりそうなので別の方法を考えます。

 

 

そもそも2年前はどう解いていたのか、もう一度提出したレポートを確認しました。充足可能性問題 充足可能性問題 - Wikipedia を解くSAT solverの一つ minisatを利用して解いており、なんと1分程度で解の一つを出していました!

 

いくつかのリテラル(真か偽となる変数のようなもの)の論理和(クローズ)の論理積がTrueとなるようなリテラルの真と偽の選び方はあるのか、といった問題です。例題は上記のwikiなど見てください。

 

このsolverであるminisatは以下のコマンドで簡単にインストールできます。

 

Linux : sudo apt-get install minisat

Mac OS : brew install minisat

 

minisatはDLLアルゴリズムを採用しています。二分探索でリテラルを真or偽と決め、その場合全てのクローズが真となるかを計算していきます。全て真であるならばクローズの論理積も真となり、その充足可能性問題が解けたということになります。

 

ではこの問題の核となる、knight tour problemを解くためにどのような充足可能性問題を作るかを考えていきます。

 

  • 時間i(1~64)において、リテラル( (i-1)*64+1 ~ i*64)を用意する(時間1(初手)は1~64, 時間2は65~128, ...)。※この64個はチェス盤の64マスに該当し、時間iは何手目に対応します。knightがいるマスに対応するリテラルを真、そうでないリテラルを偽とする。

 

f:id:partender810:20200812163040p:plain

時間1の時



前提を決めたところで、クローズを作る条件を作っていきます。

 

  1. knightは1つしか存在しない = 時間iに対応するリテラル( (i-1)*64+1 ~ i*64)のうち真は1つだけ。
  2. knightは移動距離3かつ縦横の距離は1以上で動く = 例えばリテラル1が真の(knightがそのマスにいる)場合、75(=64+11), 82(=64+18)のどちらかが真となる。
  3. 同じマスには1回しか行けない = 64で割った余りでグループ分けした時に(リテラル番号 mod 64)、真は1つだけ。

 

この条件1~3を満たすよう命題論理式を組んでいきます。

 

knightは1つしか存在しない = 時間iに対応するリテラル( (i-1)*64+1 ~ i*64)のうち真は1つだけ。

 

1-i) ある時間iにおいてknightは1個以上存在する、1-ii) ある時間iにおいてknightは1個以下存在する、と条件1を二つに分けて考えます。

 

1-i)

時間iにおけるリテラル64個の論理和は真となる。

リテラル1つでも真であれば、論理和は真となります。偽となる場合は時間iのリテラル全てが偽となり、knightが時間iにおいて存在しないこととなります。故にこの論理式が真の場合、時間iにおいてknightが1個以上存在することとなります。

 

1-ii)

時間iにおいてリテラル64個の内2つ選び、その2つの否定の論理和は真となる。

例えば、マス1にknightがいるとします。1の否定=偽, 2の否定=真となりその論理和は真となります。マス3にいる場合も1,2の否定の論理和は真です。偽となる場合は、1,2が共に真の場合です。となると、knightが時間iにおいて2個以上存在することとなります。故にこの論理式が真の場合、時間iにおいてknightが1個以下存在することとなります。

 

1-i),1-ii)の式で、ある時間iにおいてknightが一つ存在している命題論理式が立てられました。これは条件3の時にも利用します。

 

 

knightは移動距離3かつ縦横の距離は1以上で動く = 例えばリテラル1が真の(knightがそのマスにいる)場合、75(=64+11), 82(=64+18)のどちらかが真となる。

 

この例を使うと、リテラル1が真ならばリテラル75と82のどちらかが真とならなければなりません。リテラル1,75,82の論理和だと、マス1にknightがいたら真となりマス75,82にknightがいなくても真となってしまいます。なので、リテラル1の否定と75,82の論理和を取ることとします(¬1⋁75⋁82)。リテラル1が真ならば¬1は偽となるので75,82のどちらかが真となる必要があります。これは条件2そのままですね。つまり、時間iのリテラルの一つの否定と、そのリテラル(マス)から行くことができる時間(i+1)の全てのリテラル論理和全ての論理積が条件2となります。つまり、この論理式が真となる場合、knightが正しく動いていることとなります。

 

 

同じマスには1回しか行けない = 64で割った余りでグループ分けした時に(リテラル番号 mod 64)、真は1つだけ。

 

3) 64で割った余りでグループ分けし、そのグループのリテラル64個の内2つ選び、その2つの否定の論理和は真となる。

条件3では条件1でknightは1個以下を示す命題論理式を真似ます。つまり、1 mod 64のグループの場合、リテラル1,65の否定の論理和を取ります。偽となる場合はマス1,65にknightが訪れ同じマスを2回訪れていることになります。このやり方ではあるマスに1回以下訪れていることになる、ということを示しています。しかし条件1より時間iにおいてknightが1つだけ存在する命題論理式を立てたので、あるマスを訪れていないとしても他のマスに2回訪れたことにもなるので、1回以下の訪問という論理式で本問題は解けます。

故にこの論理式が真となる場合は、knightが全てのマスを1回ずつ訪れていることになります。

 

これらの条件1~3が真となればこのknight tour problemは解けたことになります!

 

 

充足可能性問題の命題論理式を立てられたので、今度はminisatで検証できるようcnfファイルをpythonで作成します。以下の命題論理式はこのようにcnfファイルに記述します。

 

命題論理式

(¬1⋁¬2⋁¬3) ⋀ (¬1⋁2⋁¬4) ⋀ (1⋁¬3⋁4) ⋀ (2⋁3⋁4)

 

f:id:partender810:20200812233312p:plain

test.cnf

 

論理和のクローズを0と改行で区切り、否定はマイナスで表現します。これを以下のコマンドで実行します。

 

minisat test.cnf ans.cnf

 

f:id:partender810:20200812233404p:plain

実行画面

 

実行すると色んな文字が出力され、最後に「SATISFIABLE」or「UNSATISFIABLE」と出ます。「SATISFIABLE」と出れば充足可能となり、その解の一つをans.cnfに出力します。

 

f:id:partender810:20200812233450p:plain

ans.cnf

 

これは、リテラル4が真、1~3が偽となる場合が解の1つとなります。

別解として{(1,2,-3,4),(1,2,-3,-4),(1,-2,3,-4),(-1,2,3,4),(-1,2,-3,4),(-1,2,-3,-4),(-1,-2,3,4)}があります。

 

では、先程の条件1~3を表す命題論理式をcnfファイルに記述するpythonプログラムを作ります。

 

 

 

 

このまま実行すると、1分程で結果が出ます。しかし、これだと開始地点を自力で決められません。先のチェス盤を1~64で表したように、開始地点のマスのリテラルをcnfファイルに追加します(マス1から始めたい場合にはcnfファイルに「1 0」を追加します)。

 

線対称、点対称を考えると以下の10マスを開始地点とし全てSATISFIABLEであれば、目的であったどの地点からでもknightがtourできるということになります。

 

f:id:partender810:20200812232517p:plain

開始地点候補の10マス

 

 

結果は開始地点によらず巡回できることが分かりました!!

 

裏でc++のプログラムも回していたので遅くなったのかもしれませんが、一番計算に時間がかかったのが10番マスからのスタートで約22時間かかりました…。朝起きたら終わっているだろうと思ったら計算途中だった時は不安になりました(笑)

 

 

開始地点のそれぞれの結果はGithubに載せておきます。

 

github.com

 

(githubに掲載するの初めてで不安…。これで合ってるかな、閲覧だけで編集できないようになっているかな…。)

 

cnfファイルを作るpythonプログラムもこのgithubに載せてあります。コメントでどの条件の命題論理式を記述しているか書いてあります。

 

 

 

ということで、knight tour problemは充足可能性問題として解いてみました。2年前の自分のレポートを読み返して、こう書いた方が分かりやすいのに等2年間で自分の国語力が変わっているなぁと実感させられることが多く良い経験になりました。こんなの解いて何になるんだ、と思われた方。御尤もです。ですが、情報系学生としてはこういう課題の方が面白く、身近なものが扱われる課題は稀なので新鮮でした。いずれはこの経験が何かに繋がるかも…?こういった問題を楽しめる方は情報系に向いているのかもしれません。つまらないと感じた方はここまで読んでいないと思うので、ここまで読んでいる方は情報系向きかもしれません(笑) 

 

こんな感じで解いてて面白いと感じた問題はまたブログに載せようと思います!他にもこんな方法があるよ、こっちの方が速いよ!などの意見も待ってます。

 

 

それでは!

 

See you next time!