picoCTF全部解く(解きたい)
未分類
picoCTFのpicoGYM(過去に出題された問題)をポイントの少ない(基本的には難易度低)ものから解いていくチャレンジ
CTF超初心者なので、他のwriteupより冗長に丁寧に書くことを心がけます。
現在17問完了。(2022/1/8)
私の環境は一般的なWindows10 LaptopにWSL(Windows Subsystem for Linux)を入れたものです。
■Obedient Cat(G,5)
ダウンロードできるファイルは拡張子がありませんが、メモ帳で開くことができます。
開くと、平文でflagが書かれていることが分かります。
■Mod 26(C,10)
ROT13という文字の変換をするとflagを獲得できます。ググると変換できるサイトがあるので、使うと速いと思います。
https://dencode.com/ja/cipher/rot13 こことか。
■Python Wrangling(G,10)
pythonコード(ende.py)の中身をみると、コマンド-e もしくは コマンド-dで実行させることで何かしらの処理がされることが分かります。
if文の中をみると、encryptとかdecryptとか書いてあるので、それぞれ暗号化と複合化のコマンドなのではないかと予想できます。
ende.pyを実行し、flagファイルをコマンド-dを使って復号化することでflagを取得できます。
入れるべきコマンドは例えば、
python ende.py -d flag.txt.en
■Wave a flag(G,10)
warmというファイルを実行しろと言われるのでlinux上で実行します。
ただし、linuxファイルにはpermissionという概念があり、ただ単純に./warmで実行するだけではpermission deniedが返ってきます。
(考えてみればWindowsにもありますね)
permissionの概念に関しては
https://qiita.com/takuyanin/items/18590600d077df707923
が詳しいです。分かりやすく載っています。
picoCTFの方のヒントだけ読むと何がなんだかわかりませんが、とにかくchmodコマンドを使ってpermissionの権限を変えると実行できるようになります。
nyan-picoctf@webshell:~$ chmod +x warm
nyan-picoctf@webshell:~$ ls
README.txt unko warm
nyan-picoctf@webshell:~$ ./warm
Hello user! Pass me a -h to learn what I can do!
nyan-picoctf@webshell:~$ ./warm -h
Oh, help? I actually don't do much, but I do have this flag here: picoCTF{b1scu1ts_4nd_gr4vy_755f3544}
nyan-picoctf@webshell:~$
■Information(F,10)
jpgファイルにはExifという情報があります。
linux上でjheadというコマンドを使うとみることが出来る………のですが、
それを使ってみてもよく分からず、眠かったので諦めてkusuwadaさんのwriteupを読みました。後はこちらから。
https://tech.kusuwada.com/entry/2021/04/08/121205
10点問題でも、もうそういう事するのか…。はい、、、精進します。
■Nice netcat...(G,15)
与えられたコマンドをlinux上で実行すると、
3桁までの数字がたくさん出てきます。
ヒントの通り、これらはASCIIコードを表していて、それぞれの数字に対応する文字があります。
対応表に基づき、数字を文字に変換していくとflagがゲットできます。
もしくは、下記のようなコードを書けば一発で出てきます。
https://wandbox.org/permlink/jTP97qZz8G2asTtN
■Transformation(R,20)
慣れていないと分かりにくいですが、
encはencordの略、ということでエンコードされたflagの文字列を表しています。
txtファイルで開くとよく分からない文字列が出てきていますが、問題文を読むと、flag[]から何らかの変換を受けて生成されたことが分かります。
これに対応した逆変換をかけることで、flagが生成できます。(要追記)
https://wandbox.org/permlink/ENGwjdGX02HHCQc8
https://tsalvia.hatenablog.com/entry/2021/04/08/110000
■Stonks(B,20)
I decided to try something noone else has before.
I made a bot to automatically trade stonks for me using AI and machine learning.
I wouldn't believe you if you told me it's unsecure! (と共にvuln.cが添付されている)
ということで、完全な死亡フラグっぽい文章があります。その通りで、このプログラムはsecureではありません。
一応実行してみると、1と2を選べと言われて、2を選ぶと即終了、1を選ぶと
What is your API token? と聞かれ、何かしらの入力をすると、それに応じて株が買われる仕組みに見えます。
(ここの例では"ako"と入力している)
~~~~~~~~~~~~~~
What would you like to do?
1) Buy some stonks!
2) View my portfolio
1
Using patented AI algorithms to buy stonks
Stonks chosen
What is your API token?
ako
Buying stonks with token:
ako
Portfolio as of Sat May 29 18:48:12 UTC 2021
2 shares of QFRC
3 shares of QQ
40 shares of YT
112 shares of YU
60 shares of P
120 shares of D
102 shares of O
35 shares of A
544 shares of NMT
Goodbye!
~~~~~~~~~~~~~~
一度購入するとそのまま終了するので、1を選んで購入した株はそれ以降見ることが出来ません。
(1を入力し購入して、その次の実行で2を選んでも何も出てこない)
よく分からないですが、
1か2を入力、1を入力した場合に何かを入力、の部分しか悪さを出来る部分が無いので、この2つのどちらかがキーとなる可能性がある、程度に思っておきます。
ソースコードは149行あり、すこし長い感じがします(競プロ視点)が、main関数から見ていきます。
(私が調べたことをつらつらと書いただけで、main関数に書いてあることは、問題の本質になんの関連もありません)
〇122行目 setbuf(stdout,NULL)->バッファを使用しない設定にしている =
https://bg1.hatenablog.com/entry/2019/03/04/210000
https://daeudaeu.com/fflush/
〇123行目 srand(time(NULL))->乱数生成の準備
〇124行目 Portfolio型変数(へのポインタ変数)を宣言 初期化条件はinitialize_portfolio()
Portfolio型変数は、structを用いて15行目から18行目で宣言されている。
Portfolio型変数は、内部にint moneyとStonk *headを持ち、さらにこのStonkもstructを用いて9行目から13行目で宣言されている。
Stonk型変数は、内部にint shares とchar symbol[MAX_SYM_LEN + 1]、さらにstruct Stonks *nextを持つ(自己参照構造体)。
自己参照構造体:http://www9.plala.or.jp/sgwr-t/c/sec15-5.html
(この問題において1ミリも重要でないので未読)
initialize_portfolioは「Portfolio型変数へのポインタ」 型の返り値を持つ関数。
中身は、Portfolio型に必要なだけメモリ領域を確保し、moneyに1-2018の適当な値を、headはNULLとした(何も株を持っていない状態)変数pを出力するもの。
もしここで確保できなかった場合main関数の125行目から128行目でエラー出力をして終わる(基本起こらない)。
〇130行目から142行目
実際に実行した時にも表示される「1か2を選択して、それに応じた処理をする」の部分。
1を押すとbuy_stonks(p)が実行され、2を押すとview_portfolio(p)が実行されるようになっている。
(main関数おわり)
次に、buy_stonks(p)の中身を見ていきます。62行目から100行目で記述されています。
63行目から65行目はpのメモリに何も値が入ってなかった時用のエラー出力。
66行目からがキーポイントで、まずchar api_buf[FLAG_BUFFER]が宣言されています。
まさに、フラグ文字列を入れるための変数であることを意味しており、臭います
67行目でFILE変数に読み込み専用モード(r)でapiファイルを展開して、
72行目でapi_bufにその内容を書き込んでいます。
すなわち、このapi_bufの中身が分かればフラグゲットです。
74行目から85行目ではお金が無くなるまで何かしらのアルゴリズムで株を選んで買っていることが分かります。
(randって文字が見えるので多分適当です)
ただ、それだとすると、この次の入力で何をしているのかが謎です。
89行目でuser_buf変数を301バイト分確保し、90行目から93行目では、scanfでuser_bufに文字を入れ、そのままprintfで出しているだけに見えます。
ただ、ここで気づくのは、printfのフォーマット指定子が全くないことです。
違和感があるので調べてみると"Format String Bug"というものが存在することが分かります。
Format String Exploitを試してみる- CTFするぞ https://ptr-yudai.hatenablog.com/entry/2018/10/06/234120
>>printfは"%s"や"%d"のようなフォーマットを見つけると,スタックから順番にデータを読み込んで処理します. したがって,bufに"%x%x%x"のような文字列が入っていると,スタック上のデータが表示されてしまいます. これがFormat String Bugと呼ばれる脆弱性なのですが,このようにスタック上のデータを漏洩してしまうだけでなく,スタック以外でもメモリ上のデータを漏洩,改竄することが可能になります.
だそうです。
そこで、
~~~~~~~~~~~~~~~~~~~~
AAAA%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
Buying stonks with token:
AAAA9b373d0804b00080489c3f7f55d80ffffffff19b35160f7f63110f7f55dc709b3618019b373b09b373d06f6369707b465443306c5f49345f74356d5f6c6c306d5f795f79336e3463646261653532ff85007df7f90af8f7f6344060aabe0010f7df2be9f7f640c0f7f555c0f7f55000ff854b78f7de358df7f555c08048ecaff854b840f7f77f09804b000f7f55000f7f55e20ff854bb8f7f7dd50f7f5689060aabe00f7f55000804b000ff854bb88048c869b35160ff854ba4ff854bb88048be9f7f553fc0ff854c6cff854c64119b3516060aabe00ff854bd000f7d98f21f7f55000f7f550000f7d98f211ff854c64ff854c6cff854bf410f7f55000f7f7870af7f900000f7f5500000118e1a9ea8047c8e000180486300f7f7dd50f7f78960804b00018048630080486628048b85
Portfolio as of Sat May 29 21:54:15 UTC 2021
1 shares of STY
1 shares of AIDB
13 shares of L
2 shares of R
4 shares of VNA
395 shares of A
816 shares of GL
Goodbye!
^C
~~~~~~~~~~~~~~~~~~~~
のように入力すると、%x指定子のおかげでスタックに入っているものが全部出てきます。
(%xは整数を16進数で出力してくれるので、整数が入っていればそのまま、char型の何かが入っていれば文字コードとして返してくれます)
フラグのフォーマットはpicoCTF{...}だったので、
p->0x70 i->0x69 c->0x63 o->0x6F
から始まる文字列がないかを探すと、、、
6f636970を含む部分があります。エンディアンの関係で順番が逆になっているのでした。
ここから始まる部分をASCIIコード変換してあげるとフラグゲットです。
変換サイトの例:https://www.rapidtables.com/convert/number/hex-to-ascii.html
リトルエンディアンで反転してるのを変換してくれるのないかな?
■Get aHEAD(W,20)
サイトを開くと、謎のBlueボタンとRedボタンが表示されています。
とりあえずソースを開くと、それぞれGETメソッドとPOSTメソッドが行われていることが分かります。
GET/POSTについてはこちらがわかりやすいです。
https://qiita.com/7968/items/4bf4d6f28284146c288f
とりあえずBurpSuiteを使えと言われているので使い方を
https://www.as-lab.net/150713/
このあたりのサイトで見てみます。
このツールはHTTPリクエストの中身を覗いたり改ざんしたり出来るようです。
ただ、パラメータを見ても怪しいところがありません。
はて…というところでヒントを見ると、「選択肢は2つじゃないよ(もっとあるよ)」的なことが書いてあります。
実際にはここでgive upしたのですが、問題のタイトルも加味して考えると、"HEAD"メソッドを試せということのようです。。。分からん。
GETやPOSTとなっているところを直接HEADに書き換えてHTTPリクエストを送ってみると、Responseとして"NAME=flag"というパラメータが返ってきていて、その中にflagがあります。
■Mind your Ps and Qs
問題文の通りRSA暗号を解く問題です。
RSA暗号の概要については https://qiita.com/YutaKase6/items/cd9e26d723809dc85928 がくわしい
ブログ記事を見ながら自習したメモ




問題で与えられるパラメータは下記の通り
メモと文字の対応が出来ていないが、c->C, n->N e->Eと読み替えます。
M = C^D (mod N)よりDが求まれば解けます。
DはD = pow(E,-1,φ(N)) = pow(E,-1,(p-1)(q-1))で、
pとqはNを素因数分解した時の2つの素数であるので、問題のキモはNを素因数分解すること。
競プロでよくやる素因数分解のアルゴリズムはO(√N)かかってしまうため、80桁以上の素因数分解には向いていないです。
世の中には
https://wacchoz.hatenablog.com/entry/2019/05/14/205306
http://www.cs.t-kougei.ac.jp/nsim/lecture/2008/ws/QS.pdf
にあるように2次ふるい法というものがあり、このオーダーのNの素因数分解を解けるらしいのですが、実装できず断念しました…
CTF的にはmseiveというツールを用いたり、http://factordb.com/ に入力することで解いてしまうことが多いようです。
(どちらも今回のNの素因数分解が出来る事を確認しました)
msieveで出た結果はこんな感じ。

最後にこの結果を用いて、平文を出しますが、平文が10進数なので16進数に直しASCIIベースで文字に変換すると、答えが得られます。
https://wandbox.org/permlink/bTDAy0ik1WqTn7NP
■Static ain't always noise (General Skills, 20pts)
バイナリファイルの中を見ろと言われます。
また、「このBASH scriptが役に立つよ」と言われるので、併せて落としてみます。
Bash scriptはシェルスクリプトの一種でlinuxで叩くようなコマンド入力を自動化するものと理解しています。
スクリプトの中身を読んでみると、
まずobjdumpコマンド(オプション: -Djと.text)を第1引数のファイルに対して行ったものを (ファイル名).ltdis.x86_64.txtとして出力します。
Bash scriptのコメントアウトにも書いてある通り、objdump -Dコマンドはバイナリファイルの逆アセンブル情報を表示し、
-j .textはテキストセクションのみを表示するオプションです。
(セクションが何なのかよく分かっていない。https://karino2.github.io/c-lesson/casm_link_load.html ここに書いてありそうな予感がします。全然読んでません)
テキストセクションには定数が入っているみたいです。Cでいうmain関数とか。(よく分かっていない)
その後、(ファイル名).ltdis.x86_64.txtが存在すれば、同じファイルに対してstringsコマンドを実施してテキストデータとして出力するように書かれています。
stringsコマンドは、バイナリファイルの中にあるテキスト情報を抜き出して一覧表示してくれるもので、出力結果を見ていくとpicoCTF{から始まる文字列があるので、これでflagゲットです。
★参考になるサイト
https://linuxcommand.net/objdump/
https://qiita.com/rsooo/items/bb91071685f447ce29db
恐らくこれが想定された解法ですが、
直接staticをバイナリエディタで開き、"picoCTF"で検索するだけでも見つかります。

■Tab, Tab, Attack (General Skills, 20pts)
上の問題と大体同じ?
■keygenme-py (Reverse Engneering, 30pts)
https://twitter.com/nya______n/status/1477368424078917632?s=20
■Matryoshka doll(Forensics, 30pts)
https://twitter.com/nya______n/status/1477385736349691904?s=20
■crackme-py(Reverse Engeneering, 30pts)
■Magikarp Ground Mission(General Skills, 30pts)
■tunn3l v1s10n(Forensics, 30pts)
■Easy Peasy(Cryptography, 40pts)
ワンタイムパスワードに関する問題。
opt.pyの中身を見ると、動作は
そのため、encrypt動作にてkeyを50000文字分まで消費した後、既知の文字列をuiとして入力し結果を出力すると、そこから鍵が逆算できます。
ui XOR key = resultですが、
XORの性質よりA XOR B = CならばA XOR C = Bが成り立つため、
ui XOR resultを計算することでkeyを計算できます。
もしくは、
https://tsalvia.hatenablog.com/entry/2021/04/08/110000#Easy-Peasy---40-points
で紹介されているように、直接"0x00"をぶちこむことで、計算しなくても鍵を出す方法があるようです。
つまり、
0 XOR A = Aが常に成り立つので、
0 XOR key = result ならば、 result = keyになるという算段です。
なお、(★)に書いてあるような仕様があるので、
例えば、「"A"を50000文字入力しresultを出して、最後の32文字分を見る」というやり方をすると不正解になります。
最後の32文字はkey[0:32]のつもりが、実際にはkey[50000:50032]が出力されているからです。(KEY_FILEが50000文字を超えていることの弊害。やっぱり問題の不備では…)
ともあれ、keyが分かりました。
最初のstartupでは、
flag XOR key = 51466d4e5f575538195551416e4f5300413f1b5008684d5504384157046e4959
であったので、
flag = key XOR 51466d4e5f575538195551416e4f5300413f1b5008684d5504384157046e4959
です。
これを計算し、最後にASCIIコードから文字列に変換すると32文字の1byte文字が得られます。
CTF超初心者なので、他のwriteupより冗長に丁寧に書くことを心がけます。
現在17問完了。(2022/1/8)
私の環境は一般的なWindows10 LaptopにWSL(Windows Subsystem for Linux)を入れたものです。
■Obedient Cat(G,5)
ダウンロードできるファイルは拡張子がありませんが、メモ帳で開くことができます。
開くと、平文でflagが書かれていることが分かります。
■Mod 26(C,10)
ROT13という文字の変換をするとflagを獲得できます。ググると変換できるサイトがあるので、使うと速いと思います。
https://dencode.com/ja/cipher/rot13 こことか。
■Python Wrangling(G,10)
pythonコード(ende.py)の中身をみると、コマンド-e もしくは コマンド-dで実行させることで何かしらの処理がされることが分かります。
if文の中をみると、encryptとかdecryptとか書いてあるので、それぞれ暗号化と複合化のコマンドなのではないかと予想できます。
ende.pyを実行し、flagファイルをコマンド-dを使って復号化することでflagを取得できます。
入れるべきコマンドは例えば、
python ende.py -d flag.txt.en
■Wave a flag(G,10)
warmというファイルを実行しろと言われるのでlinux上で実行します。
ただし、linuxファイルにはpermissionという概念があり、ただ単純に./warmで実行するだけではpermission deniedが返ってきます。
(考えてみればWindowsにもありますね)
permissionの概念に関しては
https://qiita.com/takuyanin/items/18590600d077df707923
が詳しいです。分かりやすく載っています。
picoCTFの方のヒントだけ読むと何がなんだかわかりませんが、とにかくchmodコマンドを使ってpermissionの権限を変えると実行できるようになります。
nyan-picoctf@webshell:~$ chmod +x warm
nyan-picoctf@webshell:~$ ls
README.txt unko warm
nyan-picoctf@webshell:~$ ./warm
Hello user! Pass me a -h to learn what I can do!
nyan-picoctf@webshell:~$ ./warm -h
Oh, help? I actually don't do much, but I do have this flag here: picoCTF{b1scu1ts_4nd_gr4vy_755f3544}
nyan-picoctf@webshell:~$
■Information(F,10)
jpgファイルにはExifという情報があります。
linux上でjheadというコマンドを使うとみることが出来る………のですが、
それを使ってみてもよく分からず、眠かったので諦めてkusuwadaさんのwriteupを読みました。後はこちらから。
https://tech.kusuwada.com/entry/2021/04/08/121205
10点問題でも、もうそういう事するのか…。はい、、、精進します。
■Nice netcat...(G,15)
与えられたコマンドをlinux上で実行すると、
3桁までの数字がたくさん出てきます。
ヒントの通り、これらはASCIIコードを表していて、それぞれの数字に対応する文字があります。
対応表に基づき、数字を文字に変換していくとflagがゲットできます。
もしくは、下記のようなコードを書けば一発で出てきます。
https://wandbox.org/permlink/jTP97qZz8G2asTtN
■Transformation(R,20)
慣れていないと分かりにくいですが、
encはencordの略、ということでエンコードされたflagの文字列を表しています。
txtファイルで開くとよく分からない文字列が出てきていますが、問題文を読むと、flag[]から何らかの変換を受けて生成されたことが分かります。
これに対応した逆変換をかけることで、flagが生成できます。(要追記)
https://wandbox.org/permlink/ENGwjdGX02HHCQc8
https://tsalvia.hatenablog.com/entry/2021/04/08/110000
■Stonks(B,20)
I decided to try something noone else has before.
I made a bot to automatically trade stonks for me using AI and machine learning.
I wouldn't believe you if you told me it's unsecure! (と共にvuln.cが添付されている)
ということで、完全な死亡フラグっぽい文章があります。その通りで、このプログラムはsecureではありません。
一応実行してみると、1と2を選べと言われて、2を選ぶと即終了、1を選ぶと
What is your API token? と聞かれ、何かしらの入力をすると、それに応じて株が買われる仕組みに見えます。
(ここの例では"ako"と入力している)
~~~~~~~~~~~~~~
What would you like to do?
1) Buy some stonks!
2) View my portfolio
1
Using patented AI algorithms to buy stonks
Stonks chosen
What is your API token?
ako
Buying stonks with token:
ako
Portfolio as of Sat May 29 18:48:12 UTC 2021
2 shares of QFRC
3 shares of QQ
40 shares of YT
112 shares of YU
60 shares of P
120 shares of D
102 shares of O
35 shares of A
544 shares of NMT
Goodbye!
~~~~~~~~~~~~~~
一度購入するとそのまま終了するので、1を選んで購入した株はそれ以降見ることが出来ません。
(1を入力し購入して、その次の実行で2を選んでも何も出てこない)
よく分からないですが、
1か2を入力、1を入力した場合に何かを入力、の部分しか悪さを出来る部分が無いので、この2つのどちらかがキーとなる可能性がある、程度に思っておきます。
ソースコードは149行あり、すこし長い感じがします(競プロ視点)が、main関数から見ていきます。
(私が調べたことをつらつらと書いただけで、main関数に書いてあることは、問題の本質になんの関連もありません)
〇122行目 setbuf(stdout,NULL)->バッファを使用しない設定にしている =
https://bg1.hatenablog.com/entry/2019/03/04/210000
https://daeudaeu.com/fflush/
〇123行目 srand(time(NULL))->乱数生成の準備
〇124行目 Portfolio型変数(へのポインタ変数)を宣言 初期化条件はinitialize_portfolio()
Portfolio型変数は、structを用いて15行目から18行目で宣言されている。
Portfolio型変数は、内部にint moneyとStonk *headを持ち、さらにこのStonkもstructを用いて9行目から13行目で宣言されている。
Stonk型変数は、内部にint shares とchar symbol[MAX_SYM_LEN + 1]、さらにstruct Stonks *nextを持つ(自己参照構造体)。
自己参照構造体:http://www9.plala.or.jp/sgwr-t/c/sec15-5.html
(この問題において1ミリも重要でないので未読)
initialize_portfolioは「Portfolio型変数へのポインタ」 型の返り値を持つ関数。
中身は、Portfolio型に必要なだけメモリ領域を確保し、moneyに1-2018の適当な値を、headはNULLとした(何も株を持っていない状態)変数pを出力するもの。
もしここで確保できなかった場合main関数の125行目から128行目でエラー出力をして終わる(基本起こらない)。
〇130行目から142行目
実際に実行した時にも表示される「1か2を選択して、それに応じた処理をする」の部分。
1を押すとbuy_stonks(p)が実行され、2を押すとview_portfolio(p)が実行されるようになっている。
(main関数おわり)
次に、buy_stonks(p)の中身を見ていきます。62行目から100行目で記述されています。
63行目から65行目はpのメモリに何も値が入ってなかった時用のエラー出力。
66行目からがキーポイントで、まずchar api_buf[FLAG_BUFFER]が宣言されています。
まさに、フラグ文字列を入れるための変数であることを意味しており、臭います
67行目でFILE変数に読み込み専用モード(r)でapiファイルを展開して、
72行目でapi_bufにその内容を書き込んでいます。
すなわち、このapi_bufの中身が分かればフラグゲットです。
74行目から85行目ではお金が無くなるまで何かしらのアルゴリズムで株を選んで買っていることが分かります。
(randって文字が見えるので多分適当です)
ただ、それだとすると、この次の入力で何をしているのかが謎です。
89行目でuser_buf変数を301バイト分確保し、90行目から93行目では、scanfでuser_bufに文字を入れ、そのままprintfで出しているだけに見えます。
ただ、ここで気づくのは、printfのフォーマット指定子が全くないことです。
違和感があるので調べてみると"Format String Bug"というものが存在することが分かります。
Format String Exploitを試してみる- CTFするぞ https://ptr-yudai.hatenablog.com/entry/2018/10/06/234120
>>printfは"%s"や"%d"のようなフォーマットを見つけると,スタックから順番にデータを読み込んで処理します. したがって,bufに"%x%x%x"のような文字列が入っていると,スタック上のデータが表示されてしまいます. これがFormat String Bugと呼ばれる脆弱性なのですが,このようにスタック上のデータを漏洩してしまうだけでなく,スタック以外でもメモリ上のデータを漏洩,改竄することが可能になります.
だそうです。
そこで、
~~~~~~~~~~~~~~~~~~~~
AAAA%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
Buying stonks with token:
AAAA9b373d0804b00080489c3f7f55d80ffffffff19b35160f7f63110f7f55dc709b3618019b373b09b373d06f6369707b465443306c5f49345f74356d5f6c6c306d5f795f79336e3463646261653532ff85007df7f90af8f7f6344060aabe0010f7df2be9f7f640c0f7f555c0f7f55000ff854b78f7de358df7f555c08048ecaff854b840f7f77f09804b000f7f55000f7f55e20ff854bb8f7f7dd50f7f5689060aabe00f7f55000804b000ff854bb88048c869b35160ff854ba4ff854bb88048be9f7f553fc0ff854c6cff854c64119b3516060aabe00ff854bd000f7d98f21f7f55000f7f550000f7d98f211ff854c64ff854c6cff854bf410f7f55000f7f7870af7f900000f7f5500000118e1a9ea8047c8e000180486300f7f7dd50f7f78960804b00018048630080486628048b85
Portfolio as of Sat May 29 21:54:15 UTC 2021
1 shares of STY
1 shares of AIDB
13 shares of L
2 shares of R
4 shares of VNA
395 shares of A
816 shares of GL
Goodbye!
^C
~~~~~~~~~~~~~~~~~~~~
のように入力すると、%x指定子のおかげでスタックに入っているものが全部出てきます。
(%xは整数を16進数で出力してくれるので、整数が入っていればそのまま、char型の何かが入っていれば文字コードとして返してくれます)
フラグのフォーマットはpicoCTF{...}だったので、
p->0x70 i->0x69 c->0x63 o->0x6F
から始まる文字列がないかを探すと、、、
6f636970を含む部分があります。エンディアンの関係で順番が逆になっているのでした。
ここから始まる部分をASCIIコード変換してあげるとフラグゲットです。
変換サイトの例:https://www.rapidtables.com/convert/number/hex-to-ascii.html
リトルエンディアンで反転してるのを変換してくれるのないかな?
■Get aHEAD(W,20)
サイトを開くと、謎のBlueボタンとRedボタンが表示されています。
とりあえずソースを開くと、それぞれGETメソッドとPOSTメソッドが行われていることが分かります。
GET/POSTについてはこちらがわかりやすいです。
https://qiita.com/7968/items/4bf4d6f28284146c288f
とりあえずBurpSuiteを使えと言われているので使い方を
https://www.as-lab.net/150713/
このあたりのサイトで見てみます。
このツールはHTTPリクエストの中身を覗いたり改ざんしたり出来るようです。
ただ、パラメータを見ても怪しいところがありません。
はて…というところでヒントを見ると、「選択肢は2つじゃないよ(もっとあるよ)」的なことが書いてあります。
実際にはここでgive upしたのですが、問題のタイトルも加味して考えると、"HEAD"メソッドを試せということのようです。。。分からん。
GETやPOSTとなっているところを直接HEADに書き換えてHTTPリクエストを送ってみると、Responseとして"NAME=flag"というパラメータが返ってきていて、その中にflagがあります。
■Mind your Ps and Qs
問題文の通りRSA暗号を解く問題です。
RSA暗号の概要については https://qiita.com/YutaKase6/items/cd9e26d723809dc85928 がくわしい
ブログ記事を見ながら自習したメモ




問題で与えられるパラメータは下記の通り
Decrypt my super sick RSA:
c: 62324783949134119159408816513334912534343517300880137691662780895409992760262021
n: 1280678415822214057864524798453297819181910621573945477544758171055968245116423923
e: 65537
メモと文字の対応が出来ていないが、c->C, n->N e->Eと読み替えます。
M = C^D (mod N)よりDが求まれば解けます。
DはD = pow(E,-1,φ(N)) = pow(E,-1,(p-1)(q-1))で、
pとqはNを素因数分解した時の2つの素数であるので、問題のキモはNを素因数分解すること。
競プロでよくやる素因数分解のアルゴリズムはO(√N)かかってしまうため、80桁以上の素因数分解には向いていないです。
世の中には
https://wacchoz.hatenablog.com/entry/2019/05/14/205306
http://www.cs.t-kougei.ac.jp/nsim/lecture/2008/ws/QS.pdf
にあるように2次ふるい法というものがあり、このオーダーのNの素因数分解を解けるらしいのですが、実装できず断念しました…
CTF的にはmseiveというツールを用いたり、http://factordb.com/ に入力することで解いてしまうことが多いようです。
(どちらも今回のNの素因数分解が出来る事を確認しました)
msieveで出た結果はこんな感じ。

最後にこの結果を用いて、平文を出しますが、平文が10進数なので16進数に直しASCIIベースで文字に変換すると、答えが得られます。
https://wandbox.org/permlink/bTDAy0ik1WqTn7NP
■Static ain't always noise (General Skills, 20pts)
バイナリファイルの中を見ろと言われます。
また、「このBASH scriptが役に立つよ」と言われるので、併せて落としてみます。
Bash scriptはシェルスクリプトの一種でlinuxで叩くようなコマンド入力を自動化するものと理解しています。
スクリプトの中身を読んでみると、
まずobjdumpコマンド(オプション: -Djと.text)を第1引数のファイルに対して行ったものを (ファイル名).ltdis.x86_64.txtとして出力します。
Bash scriptのコメントアウトにも書いてある通り、objdump -Dコマンドはバイナリファイルの逆アセンブル情報を表示し、
-j .textはテキストセクションのみを表示するオプションです。
(セクションが何なのかよく分かっていない。https://karino2.github.io/c-lesson/casm_link_load.html ここに書いてありそうな予感がします。全然読んでません)
テキストセクションには定数が入っているみたいです。Cでいうmain関数とか。(よく分かっていない)
その後、(ファイル名).ltdis.x86_64.txtが存在すれば、同じファイルに対してstringsコマンドを実施してテキストデータとして出力するように書かれています。
stringsコマンドは、バイナリファイルの中にあるテキスト情報を抜き出して一覧表示してくれるもので、出力結果を見ていくとpicoCTF{から始まる文字列があるので、これでflagゲットです。
★参考になるサイト
https://linuxcommand.net/objdump/
https://qiita.com/rsooo/items/bb91071685f447ce29db
恐らくこれが想定された解法ですが、
直接staticをバイナリエディタで開き、"picoCTF"で検索するだけでも見つかります。

■Tab, Tab, Attack (General Skills, 20pts)
上の問題と大体同じ?
■keygenme-py (Reverse Engneering, 30pts)
https://twitter.com/nya______n/status/1477368424078917632?s=20
■Matryoshka doll(Forensics, 30pts)
https://twitter.com/nya______n/status/1477385736349691904?s=20
■crackme-py(Reverse Engeneering, 30pts)
■Magikarp Ground Mission(General Skills, 30pts)
■tunn3l v1s10n(Forensics, 30pts)
■Easy Peasy(Cryptography, 40pts)
ワンタイムパスワードに関する問題。
opt.pyの中身を見ると、動作は
- 外部から"key"と"flag"を読み込む keyの長さは50000っぽい?
- flagは文字列として、keyは"rb"指定なのでバイナリ(16進数)として取得。
- startup動作
- flagの長さ分だけkeyを取ってくる。例えばflagの長さが32ならkeyのうち1文字目から32文字目を取ってくる。
- 19行目にて、resultの中にlistとして『「flagを1文字ずつとったものをバイナリに変換したもの」と「keyを1文字ずつとったもの」のXORをとったもの』を入れていく
- この結果を表示。shell上で下記のように表示される。文字を数えると32文字。(1byte文字が32文字なので、バイナリにすると64文字出力されている。例えば最初の"51"は"Q")
- This is the encrypted flag!51466d4e5f575538195551416e4f5300413f1b5008684d5504384157046e4959
- encrypt動作(1回目)
- 何か任意の文字列を入力する。= ui
- uiとkeyに対してstartup動作と同様のことを行う。
- ただし、keyは再び最初から使うわけではなく、startupで使用したkeyの次の文字から使う。
- 例えば、startup動作が上記の例だとして、len[ui] = 10なら、ここで使われるkeyは33文字目から42文字目まで。(key[32:42])
- 同様にuiとkeyでXORを取ったものを表示
- encrypt動作(2回目以降)
- 同様だが、keyは直前に使ったところの次から使用する。
- (正確には1回目でも起こるが)使用したkeyが50000文字に達すると、(34行目から36行目の記述)にあることが起こる。
- (★)ずっと勘違いしていたのですが、恐らくKEY_FILEの中身は実際には50000文字より長いので、このif文の中身が実行されるとき、実際にはkey[:stop]の部分は出てこないものと思われます。(問題の不備では???)
- (★)もう少し具体的に言うと、key_location=49995(つまり49996文字目)のとき、ui = 10の文字列を入力すると、ソースコード上はkey[49995:50000]+key[0:5]が出てくるように見えますが、実際にはlen[key]は50000より長いので、key[49995:50005]の文字が出力されていると思われます。
そのため、encrypt動作にてkeyを50000文字分まで消費した後、既知の文字列をuiとして入力し結果を出力すると、そこから鍵が逆算できます。
ui XOR key = resultですが、
XORの性質よりA XOR B = CならばA XOR C = Bが成り立つため、
ui XOR resultを計算することでkeyを計算できます。
もしくは、
https://tsalvia.hatenablog.com/entry/2021/04/08/110000#Easy-Peasy---40-points
で紹介されているように、直接"0x00"をぶちこむことで、計算しなくても鍵を出す方法があるようです。
つまり、
0 XOR A = Aが常に成り立つので、
0 XOR key = result ならば、 result = keyになるという算段です。
なお、(★)に書いてあるような仕様があるので、
例えば、「"A"を50000文字入力しresultを出して、最後の32文字分を見る」というやり方をすると不正解になります。
最後の32文字はkey[0:32]のつもりが、実際にはkey[50000:50032]が出力されているからです。(KEY_FILEが50000文字を超えていることの弊害。やっぱり問題の不備では…)
ともあれ、keyが分かりました。
最初のstartupでは、
flag XOR key = 51466d4e5f575538195551416e4f5300413f1b5008684d5504384157046e4959
であったので、
flag = key XOR 51466d4e5f575538195551416e4f5300413f1b5008684d5504384157046e4959
です。
これを計算し、最後にASCIIコードから文字列に変換すると32文字の1byte文字が得られます。
スポンサーサイト