シアン化備忘録

技術系とか諸々

TsukuCTF 2023 Writeup

Kawaii Raz0r Bladesでソロ参加し、18位でした。


OSINT

airport

つくしくんは、旅の思い出を振り返っていましたが、この写真はどこの空港かわからなくなりました。
ここはどこの空港か教えてくれませんか?
Flagフォーマットは TsukuCTF23{空港の3レターコード(IATA)} です。

Google Lensに投げるとブログが見つかる。

ブログを参考に伊丹空港のレターコードであるITMを入れると正解。

TsukuCTF23{ITM}

castle

この前、お城に行ってこの写真を取ってきたんだ!
どこにあるかわかるかい?
ラグのフォーマットは、TsukuCTF23{緯度_軽度} です。
小数点は第三桁まで有効とします。

Google Lensに投げると姫路にある太陽公園であることが分かる。

Google Mapを参考に緯度経度を調整して提出。

TsukuCTF23{34.886_134.630}

eruption

つくしくんは旅行に行ったときに噴火を見ました。噴火の瞬間を実際に見たのは初めてでしたが、見た日付を覚えていません。
つくしくんが噴火を見た日付を写真の撮影日から特定して教えてください。
撮影場所が日本なのでタイムゾーンはJSTです。フラグの形式は TsukuCTF23{YYYY/MM/DD} です。

Google Lensに投げると、似た画像が掲載されているブログを発見。

どうやらJANOG49 in 鹿児島の開催期間中に噴火したとのことなので、Xで「JANOG 噴火」で検索すると2022年1月28日であることが分かる。

TsukuCTF23{2022/01/28}

location_for_what

とある場所を友達と探索していると、「ここ、何かの映画の聖地だった気がするけど、名前忘れちゃった......」とのこと。
シュッと特定して教えてあげよう!
Flagの形式は TsukuCTF23{映画のタイトル} です。

画像サイズが大きかったので一部を切り抜いています。

Google Lensに投げると、こちらのブログを発見。

言の葉の庭だそう。今度見てみようかな。

TsukuCTF23{言の葉の庭}

green_bridge

この写真が撮影されたのはどこですか...?
Flagフォーマットは TsukuCTF23{緯度_経度} です。
端数は少数第4位を四捨五入して小数点以下第3位の精度で回答してください。

画像サイズが大きかったので一部を切り抜いています。

Google Lensに投げると、こちらのブログからもみじ谷大橋であることが分かる。

Google Mapで写真の地形を見ながら、それらしい緯度経度を提出すると正解。

TsukuCTF23{36.956_139.880}

perfume

とある施設でいろいろな香水を見かけたが、施設の場所が思い出せない。
この施設の場所を調べ、教えてほしい。
ラグはTsukuCTF23{緯度_経度}であり、小数点第三桁まで有効である。

Google Lensに投げると、こちらのブログから大分香りの博物館であることが分かる。

Google Mapで調べて緯度経度を提出。

TsukuCTF23{33.311_131.488}

mab

mab.main.jpが使用しているレンタルサーバサービスを特定し、そのWebサイトのドメイン名を答えてください。
Flagフォーマットは TsukuCTF23{ドメイン名}です。

nslookupでIPを正引きしてShodanにかけるだけ。

TsukuCTF23{https://lolipop.jp}

tsukushi_estate

つくし君が写真に写っているビルにオフィスを構えたいらしいのだけど、築年数が少し心配......
つくし君の代わりに調査してください!
Flagの形式は TsukuCTF23{築年_月} です。
例えば、2022年3月に出来たビルであれば、 TsukuCTF23{2022_03} になります。

つくし不動産の公式サイトに飛んで、貸オフィスの一覧を見てみると写真と似た物件を発見。

築年月を見て提出。

TsukuCTF23{1983_03}

travel_with_tsukushi

旅が好きなつくしくんは、空港の写真からそれがどこの空港かすぐにわかります。
つくしくんからの挑戦状!
これがどこの空港かわかるかな?
Flagフォーマットは TsukuCTF23{空港の3レターコード(IATA)} です。

Google Lensに投げるとこちらのブログを発見。

クアラルンプールであることが分かるので、クアラルンプール空港のレターコードであるKULを提出して正解。

TsukuCTF23{KUL}

kiZOU

ここは日本で一番のリゾート地!少し歩くと目の前に素敵な像が見えたから写真を撮ったつもりだったんだけど、
見返したら端っこしか写ってない!困ったなぁ、この像についてもっと知りたかったんだけどなぁ。
僕の代わりにこの像について調べてくれないか?
ラグ形式は TsukuCTF23{像を寄贈した人物の名前} です。

画像をよく見ると、au style NAHAという文字が見つかり、那覇auショップであることが分かる。

また、au style NAHAで検索すると、パレット久茂地に店を構えていることも分かる。

ストリートビューで見てみるとシーサー像を発見。

「パレット久茂地 シーサー像」で検索するが寄贈者は見つからなかったので、Xで改めて調べるとこちらのツイートが見つかる。

TsukuCTF23{上原清善}

big_statue

大きなドリアンだ!どこにあるんだろう??
ラグの形式は TsukuCTF23{緯度_経度} です。
例えば、この像が東京の渋谷駅にある場合、フラグは TsukuCTF23{35.6580_139.7016} となります。

Google Lensに投げても見つからなかったので、画像で見える「榴梿王 利陞」を検索してみると、Lexas Durian KingのFacebookが見つかる。

また、こちらのTikTokから店の前に画像のドリアン像があることが分かる。

よって、ストリートビューからドリアン像の位置の緯度経度を取得する。

草に紛れて見にくい

TsukuCTF23{1.3623_103.8872}

TrainWindow

夏、騒音、車窓にて。
ラグのフォーマットは、TsukuCTF23{緯度_経度}です。
緯度経度は小数第五位を切り捨てとします。

Google Lensにかけると、熱海であることは分かるが詳細な位置までは分からない。

ただ、こちらのブログより、網代駅付近であることが分かる。

よって、熱海~網代駅までの路線沿いで画像と似たような風景になる場所をストリートビューを手掛かりに探すとここがヒット。

TsukuCTF23{35.0641_139.0666}

CtrlAltPrtSc

仕事中にCtrl + Alt + PrtScでウィンドウのスクリーンショットを撮ったよ。
つくし君がサボって使用していたサービスの名前を答えよ。 フラグはTsukuCTF23{サービスの名前}の形式です。

サボって利用するサービスということと左上のロゴと文字よりYoutubeと推測するとフラグだった。

これ勘以外の解き方あるんですか

TsukuCTF23{Youtube}

laser

光源の座標を正確に教えてください。
ラグフォーマットは、TsukuCTF23{緯度_経度}です。 小数点以下5位を切り捨てて、小数点以下4桁で答えてください。

範囲を調整しながらGoogle Lensで調べると、こちらのツイートが見つかる。

リプライで梅田換気塔に設置された光源であることが分かる。

こちらのサイトを参考にしながらGoogle Mapから座標を調整して提出。

TsukuCTF23{34.7016_135.4990}

3636

ここはどこ...?
Flagフォーマットは TsukuCTF23{緯度_経度} です。
端数は少数第四位を四捨五入して小数点以下第三位の精度で回答してください。

適当に「3636 電話番号 ed jp」で検索すると、とうみょうこども園がヒットする。

ストリートビューで近辺を探索すると、この場所であることが分かる。

ちょうど看板の場所になるように緯度経度を調整して提出。

TsukuCTF23{37.501_139.928}

Yuki

雪、無音、窓辺にて。
ラグのフォーマットは、TsukuCTF23{緯度_経度}です。
緯度経度は小数第四位を切り捨てとします(精度に注意)。

範囲を調整してGoogle Lensに投げると、定山渓という札幌の温泉地であることが分かる。

観光サイトの宿泊施設をMapなども見ながら調べていくと、定山渓ビューホテルが地形的に画像と近いことが分かる。

また、ホテル内のカフェ サンリバーというカフェの画像が室内の机、椅子と一致していることが分かる。

Google Mapを見ながら緯度経度を調整して提出。

TsukuCTF23{42.968_141.167}

tsukushi_no_kuni

かつて、筑紫国を統治していた国造の一人が乱を起こした。
その子孫の一人が、ある天皇と同一人物である説が提唱されている。
その子孫の名前を TsukuCTF23{} で囲んで答えよ。

筑紫国 国造 乱」で検索すると磐井の乱が見つかり、筑紫君磐井という人物が起こしていることが分かった。

「筑紫君磐井 子孫 天皇」で適当に複数記事を回ると、このブログ天武天皇についての記載が散見される。

よく分からないが適当に天武天皇で提出するとフラグだった。提唱されているソースが何処なのかは不明。

TsukuCTF23{天武天皇}

free_rider

https://www.fnn.jp/articles/-/608001
私はこのユーチューバーが本当に許せません!
この動画を見たいので、元のYouTubeのURLを教えてください。
また、一番上の画像(「非難が殺到」を含む)の再生位置で指定してください。
ラグフォーマットは、
TsukuCTF23{https://www.youtube.com/watch?v=REDACTED&t=REDACTEDs}

外国人の新幹線無賃乗車動画の元動画のURLを探す問題。

「新幹線 無賃乗車 動画」で検索すると、この記事からFidiasというYoutuberであることが分かる。

元動画のタイトルを知りたいため、「Fidias Japan Travel」で調べるとこの記事から、"I Travelled Across Japan For Free"という動画であることが分かった。

消される前にツイートで拡散されているだろうと推測し、Xで「I Travelled Across Japan For Free」で調べるとツイートが見つかり、動画が消去されていることからこれが元動画のURLであると推測。

次に再生位置を知りたいが、この手の炎上動画は大抵第三者に再投稿されているので、Googleでタイトルを調べると再投稿された動画が見つかる。

問題文の記事と同じ部分は2:56 = 176sであることが分かるため、https://www.youtube.com/watch?v=Dg_TKW3sS1U&t=176sがフラグ。

TsukuCTF23{https://www.youtube.com/watch?v=Dg_TKW3sS1U&t=176s}

river

弟のたくしから、「ボールが川で流されちゃった」と写真と共に、連絡がきた。
この場所はどこだ?
Flagフォーマットは TsukuCTF23{緯度_経度} です。
端数は少数第5位を切り捨てて小数点以下第4位の精度で回答してください。

画像サイズが大きかったので一部を切り抜いています。

タコ負けして最近パチ禁をしているはずなのですが、ニューギンの専用駐車場が真っ先に目についたのでこれを手掛かりに見ていく。

ニューギンの会社概要から支店や営業所の住所をGoogle Mapで見ていくと、周りの風景から鹿児島営業所であることが分かる。

Google Mapから緯度経度を調整し、よっしゃあ漢唄を歌いながらフラグを提出。

TsukuCTF23{31.5757_130.5533}

broken display

表示が壊れているサイネージって、写真を撮りたくなりますよね!
正しく表示されているときに書かれている施設名を見つけて提出してください!
ラグ形式: TsukuCTF23{◯◯◯◯◯◯◯◯IYA_◯◯◯◯◯◯S}

後ろの方に日能研と駅のようなものが見える。

よって、〇〇宮かつローマ字表記での文字数が一致する地名 + 駅前に日能研がある場所を探していく。

地名コレクション日能研の教室案内を見ていくと、西宮が見つかる。

「NISHINOMIYA」で検索すると西宮ガーデンズというショッピングセンターが見つかり、ローマ字表記の文字数が一致するためここであると推測し、提出。

TsukuCTF23{NISHINOMIYA_GARDENS}

stickers

この画像が撮影された場所を教えてください!
Flagフォーマットは TsukuCTF23{緯度_経度} です。
ただし、小数点4桁の精度で答えてください。

画像サイズが大きかったので一部を切り抜いています。

Google Lensに投げても何も出てこない。

千社札らしきものが貼られており熱海プリンのプリンカーが写っていることから、熱海の神社を中心に探していくが、全く見つからない。

また、背景に「CI EC」が見えるのでそれらしき企業も探すが何も出てこない。(どうやらFUJITECだったみたいです。)

仕方ないので、もう一度Google Lensを用いて画像 + 熱海で検索すると、熱海七湯 河原湯が見つかる。

Google Mapより緯度経度を調整して提出。

TsukuCTF23{35.0967_139.0747}

RegexCrossword

クロスワードを解いてみて!
これを作った会社の本社の郵便番号をハイフンありで答えてね!!

Google Lensで調べても出てこない。

何らかのグッズ?か備品であると推測し、Xで「正規表現 クロスワード」と検索すると、このツイートが見つかる。

記載されているブログから会津大学の学食のナプキンであることが分かる。

もう一度Xで「会津大学 正規表現 クロスワード ナプキン」で検索すると、このツイートからEyes,Japanが制作したことが分かるので、Google検索で「Eyes,JAPAN」と検索し、出てきた郵便番号がフラグ。

TsukuCTF23{965-0872}

flower_bed

花壇の先にQRコードのキューブがあるようですね。友人曰く、モニュメントの近くに配置されているものらしいです。
こちらのQRコードが示すURLを教えてください! リダイレクト前のURLでお願いします!
Flagの形式は TsukuCTF23{URL} です。例えば、https://sechack365.nict.go.jp がURLなら、
TsukuCTF23{https://sechack365.nict.go.jp} が答えになります。

画像サイズが大きかったので一部を切り抜いています。

QRコードが書いてある面に、「〇〇 Prefectual Civil Hall and 〇〇 House Official Site」という文章がある。

Googleで「Prefectual Civil Hall and House Official Site」と調べると、旧福岡県公会堂貴賓館公式サイトが見つかる。

ただしこのURLがフラグではなかったため、もう少し調査する必要がある。

Google Mapを見てみると、近くにFUKUOKAモニュメントというモニュメントが存在し、問題文からこの近くで撮影されていることが分かる。

与えられた画像ではよく分からなかったが、どうやら立体のモニュメント上にQRコードが存在しているらしく、モニュメントを右側から撮影した画像からQRコードを読み取れれば良さそう。

一応このツイートが見つかるが、解像度が低く読み取れない。

仕方ないので、unlimited:waifu2xで無理やり4倍に拡大させるとうまくQRコードを読み込むことが出来た。

CPUに悲鳴を上げさせて作成した拡大QRコード

TsukuCTF23{http://www.fukuokaken-kihinkan.jp}

grass_court

しばらく使われていないテニスコートのようだ。
この日本にあるテニスコートの場所はどこだろう。
ラグの形式は TsukuCTF23{緯度_経度}です。
小数点以下5位を切り捨てて、小数点以下4桁で答えてください。

テニスコートだけでは特定出来なさそうなので、後ろにあるパラボラアンテナに注目する。

画像+パラボラアンテナでGoogle Lensで検索すると、水沢VLBI観測所がヒットする。

Google Mapで見てみると、風の又三郎テニスコートとテニスコート前の木の本数からここであると推測し、画像から緯度経度を調整して提出。

TsukuCTF23{39.1350_141.1325}

fiction

「座標を教えてくれ」
ラグフォーマットは、TsukuCTF23{緯度_経度}です。
小数点以下5位を切り捨てて、小数点以下4桁で答えてください。

何故VALORANT?と思いながらGoogle Lensに投げる。

すると、Sunsetというマップであることが分かる。(最近やってないので本当に知らんかった)

fandomを見ると座標である34°2'2" N 118°12'16" Wが見つかる。

どうやら実際の地域をモデル?にしているらしく、Google Mapに座標を張り付けて検索すると緯度経度が分かるので提出。

TsukuCTF23{34.0338_-118.2044}

hunter

名前をメールで聞こうとしたところ、相手のGmailの一部が分からなくなってしまいました。
大変お忙しいところ恐縮ですが、暇なときに調査してください。
qeinijo#iby#@gmail.com
# が不明な部分です。
なお、外部サービスに短期間で多くのアクセスをしないようにしてください。

正確なメールアドレスが分からないのでEpieosなども使えなさそう。

ということでgmailのメールアドレスに利用可能な文字(小文字アルファベット + 数字 + .)で有り得る37*37=1369通りのメールアドレスのリストを作成する。

GHuntを用いてリスト内のメールアドレスを調べ、有効なアカウントが見つかるまでループさせるスクリプトを作成した。

有効でないメールアドレスは、The target wasn't found.と出力されるだけであり、有効な場合はGoogleの各アクティビティが表示される。

よって、出力内の"Google"という文字の有無で判定させた。

import string

ALPHA = string.ascii_lowercase + string.digits + "."

email = ""

with open("email_list.txt","wt") as f:
    for a1 in ALPHA:
        for a2 in ALPHA:
            email = "qeinijo" + a1 + "iby" + a2 + "@gmail.com" + "\n"
            f.write(email)
import time
import os

email_list = []
with open("email_list.txt") as f:
    for line in f:
        email_list.append(line.rstrip())

for mail in email_list:
    print(mail)
    time.sleep(1)
    process = os.popen("ghunt email " + mail)
    ret = process.read()
    process.close()
    if "Google" not in ret:
        continue
    else:
        print(ret)
        break

40~50分ぐらい回すと、qeinijo.iby8@gmail.comが有効なアカウントであり、出力内のName欄にフラグであるTsukuCTF23{GHun7_i5_u5efu1}が見つかる。

TsukuCTF23{GHun7_i5_u5efu1}

twin

ハッカーは独自に収集した大量の個人情報を、とあるWebサイト上で2023年11月23日に投稿した。
我々はこの投稿IDがKL34A01mであるという情報を得た。ハッカーGitHubアカウントを特定せよ。

ダークウェブ要素は恐らくないであろうこと、ハッカー・投稿・個別IDからPastebinと推測。

Pastebinに飛ぶと投稿が見つかる。

GEMINI6512の他の投稿を見ると、ruby製のクワインが見つかるが、プログラム自体を調べてもあまり意味はなさそう。

githubアカウントを特定出来ればいいので、githubリポジトリにそのまま投稿されてるのではと推測し、githubで"Tsuine"と検索するとリポジトリがヒット。

そのままgithubアカウントを提出すればOK。

TsukuCTF23{gemini5612}

First Bloodでした:)

Web

basic

保護されていない通信ではパスワードはまる見えダゾ!
e.g. パスワードが Passw0rd! の場合、フラグは TsukuCTF23{Passw0rd!} となります。"

pcapファイルが渡される。

basic認証だろうと当たりをつけてHTTPストリームを見ると、AuthorizationにBase64エンコードされている認証情報が見つかる。

Cyberchefで復号してパスワードを提出。

TsukuCTF23{2929b0u4}

misc

what_os

とある研究所から、昔にシェル操作を行った紙が送られてきた来たんだが、
なんのOSでシェルを操作しているか気になってな。
バージョンの情報などは必要ないから、OSの名前だけを教えてくれないか?
にしても、データとかではなく紙で送られて来たんだ。一体何年前のOSなんだ。。。
送られてきた紙をダウンロードして確認してほしい。

シェル操作をしている様子のテキストファイルが配布される。

適当にas2 getty glob init msh passwd std0とかで検索すると、unix-jun72というGoogle Code Archiveが見つかる。

unix-jun72で調べると、こちらの記事がヒット。

UNIX 1st editionってことはUNIX?と推測し提出すると正解だった。

TsukuCTF23{Unix}

build_error

怪盗シンボルより、以下の謎とき挑戦状が届いた。
怪盗シンボルだ!
メールに3つのファイルを添付した。
この3つのファイルを同じディレクトリに置き、makeとシェルに入力し実行するとビルドが走るようになっている。
ビルドを行い、標準出力からフラグを入手するのだ!
追記:ソースコードは秘密
怪盗シンボルはせっかちなので、ビルドできるかチェックしているか不安だ。。。 取りあえずチャレンジしてみよう。
FlagフォーマットはTsukuCTF23{n桁の整数}になります。

Makefile, main.o, one.oというファイルが渡される。

普通にmakeしても失敗するので、オブジェクトファイルをghidraでデコンパイルしてみる。

undefined8 main(void)

{
  int local_34;
  long local_30;
  long local_28;
  long local_20;
  
  local_30 = 0xc;
  local_28 = 0xb;
  local_20 = 0x4b;
  one_init();
  for (local_34 = 0; local_34 < local_28; local_34 = local_34 + 1) {
    if (local_34 < local_30) {
      local_20 = local_20 + 1;
    }
    if (local_20 < local_34) {
      local_28 = local_28 + 1;
    }
    local_30 = local_30 + 1;
  }
  local_20 = local_20 + local_30 + local_28;
  if (local_20 == c + a + b) {
    printf("flag is %ld\n",local_20);
  }
  else {
    puts("please retry");
  }
  return 0;
}
void one_init(void)

{
  int local_c;
  
  a = 0xc;
  b = 0xb;
  c = 0x4b;
  for (local_c = 0; (ulong)(long)local_c < b; local_c = local_c + 1) {
    if ((ulong)(long)local_c < a) {
      c = c + 1;
    }
    if (c < (ulong)(long)local_c) {
      b = b + 1;
    }
    a = a + 1;
  }
  return;
}

ビルド出来るように弄らなくてもそのままPythona+b+cを求めた方が楽そうなので再現コードを書いてフラグを求める。

def one_init(a,b,c):
    a = 0xc
    b = 0xb
    c = 0x4b
    i = 0

    for i in range(b):
        if i < a:
            c += 1

        if c < i:
            b += 1

        a += 1

    return a,b,c


a,b,c = one_init(0,0,0)
flag = a+b+c

print("TsukuCTF23{" + str(flag) + "}")

TsukuCTF23{120}

content_sign

どうやら、この画像には署名技術を使っているらしい。
この署名技術は、画像に対しての編集を記録することができるらしい。
署名技術を特定し、改変前の画像を復元してほしい。
Flag形式はTsukuCTF23{<一個前に署名した人の名前>&<署名した時刻(ISO8601拡張形式)>}です。
例えば、一個前に署名した人の名前は「Tsuku」で、署名した時刻が2023/12/09 12:34:56(GMT+0)の場合、
ラグはTsukuCTF23{Tsuku&2023-12-09T12:34:45+00:00}です。
なお、タイムゾーンはGMT+0を使用してください。

exiftoolで与えられたpngファイルを見てみると、c2paという文字が散見される。

どうやら画像の認証を行う技術規格だそう。

c2patoolというツールで署名を見てみると2つ署名が見つかるが、改変前の画像の復元なので時間が早い1つ目の署名を答えれば良い。

1つ目の署名

2つ目の署名

1つ目の署名者はTSUKU4_IS_H@CKERであることが分かるので、1つ目の署名の時刻と組み合わせてフラグ。

TsukuCTF23{TSUKU4_IS_H@CKER&2023-12-08T13:00:26+00:00}

Open xINT CTF 2023 Writeup

2年ぶりの記事らしい

今年も開催されるとのことで参戦。オンラインで解ける14問中10問解いて4位でした。


[注意]
個人の情報となりうるもの(個人名、電話番号、アカウント名やそのリンク)は意図的に伏せています。
何か問題が発生した場合は記事の削除および編集を行います。

解けた問題

GEO

Amusement

ここの施設の名前は?

Google画像検索で出てきた。

mural

この壁画が描かれた建物の位置座標を答えてください。
フラグ形式: 7桁のplus code
(画像出典: https://iranprimer.usip.org/blog/2022/feb/09/irans-revolution-43-politics)

出典の画像をスクショして画像検索すると、Flickrに投稿された写真がヒット。

Enghelab Square, Tehranとあるので、Google Mapで調べて画像の風景と合致する所を調べる。

Flickrの画像をよく見ると左端に駅が写っているのでそれを参考にすると以下の場所あたり。

あとはプラスコードを打つだけなので、適当にGuessingして建物のプラスコードを入れたら正解。

Esta rico!

このお店の電話番号は?

机の上にある料理名で検索しても出なかったので冷蔵庫のシールからGuessingする。

橙色のシールに「055」からはじまる電話番号らしき番号が見えるため、「055 市外局番」で調べる。

すると、静岡県御殿場市駿東郡小山町の市外局番であることが分かる。

また、画像内のレジ付近に「サルチーニャ」(サルティーニャ?)が売っているため、「サルティーニャ」で調べるとボリビアの料理であることが分かる。

よって、静岡県御殿場市 ボリビア料理」で調べると2番目にホットペッパーグルメのページが出てくる。

店内の様子から画像と一致していることが分かるので、その店の電話番号が正解。

WHOIS

neko

neko.vn の登録日付はいつ?
フラグ形式: yyyy-mm-dd
hint: ほかのサイトで探してみて。

ヒント通り、「vn domain whoisで調べて出てきた下記サイトでwhoisをかけると登録日付が出てくる。

https://vnnic.vn/en/whois-information?lang=en


MISC

Aircraft

この機体のモデル名は?
フラグ形式: [A-Z]+-\d+[A-Z]+
https://1drv.ms/v/s!AmBkM-TKj1UtisAkIbSDFG8xxXWAOg?e=tJ9WkV

4つのプロペラがついた航空機の動画が渡される。

スクショして画像検索したら「C-130」と出たので提出してみたがダメ。

形状は似ているのでおそらく同型機であると予想し、C-130のwikipediaに記載されている機体で映像と似ているものを入れていく。

「C-130J」が正解だった。

C-130 (航空機) - Wikipedia


AI

REVERSE AI

この生成AI画像は、ある文学作品の冒頭部分を画像イメージにしたものである。この文学作品のタイトルを答えよ。

推測できるような学がないため、「2人の猟師 3匹の白い犬 文学作品」で調べる。
すると、注文の多い料理店がヒットしたのでこれが正解。


BUS

BUS

このバスの座標を求めよ。
フラグ形式: Nxx.xxx_Exxx.xxx

ひとまずバスの部分で画像検索すると、日光市営バスであることが分かり、主に足尾JR日光駅で運行されている車両であることが分かる。

https://www.nikko-kotsu.co.jp/images/localbus/ashiojrnikkosenakakuraasiosen.pdf

また、カーナビ部分に国道122号線の標識が出ており、上記pdfからバスの行先表示をGuessingすると、JR日光駅である可能性が高い。

よって、足尾JR日光駅線の路線内で、JR日光駅方面に向かう国道122号線の部分をしらみつぶしに探せばよい。

奥の方にある青と赤の旗や、道路上の速度制限表示を参考にしながら似た風景を探すと、以下の場所がヒット。

www.google.com

ピン差しして緯度経度を微調整すれば正解。


SOCIAL

cosplayer

下記のURLに投稿された、恐竜に扮したCCさくらコスプレイヤーの方は昔、
人間のCCさくらコスプレイヤーの方と2ショットを撮っています。
人間のCCさくらのコスプレをしていた方のX(Twitter)ユーザ名を答えてください。
https://x.com/MikeCovers/status/979804843543056384?s=20

ツイートより、「card-raptor sakura animeboston2018」で検索すると、一番上にfacebookの投稿が見つかる。

投稿にはご丁寧に恐竜CCさくらのご本人のアカウントが掲載されているので飛ぶ。

次に、写真からタグ付けされている写真を見ると、人間のCCさくらとのツーショット画像の投稿がある。

これも同様に相手のアカウントが掲載されているため飛んでみる。

相手のFBアカウントの投稿にはXのアカウントに関する情報は見当たらなかったため、アイコンをスクショして画像検索したらアカウントを発見出来た。

pet

この場所の開園式を撮影したカメラマンは、
今は引退してゴルフを楽しむなど余生を謳歌している。
そんな彼を長年支えてきたペットの名前は?

とりあえず画像検索してみると、バンクーバーPortal Parkという公園であることが分かる。

「Vancouver Portal Park Opening Ceremony」で画像検索をかけると、開園式の写真が掲載されたサイトがヒット。

https://searcharchives.vancouver.ca/unidentified-man-speaking-at-portal-park-opening-4

サイト内にこの写真の権利者の氏名が載っているため、「(氏名) photographer」で検索すると、この写真家がオーナーである会社のLinkedinが出てくる。

さらに会社名で検索すると、FBのアカウントが見つかる。

自己紹介欄にゴルフについて言及しているためこれが本人だと推測し、写真欄を見るとペットの誕生日を祝う投稿がある。

その投稿にペットの名前が記載されていたため、これが答え。


CRYPTO

Bitcoin

2021年11月に結婚したIlyaが所有していたとみられるBitcoinのアドレスは?

「Ilya who married in 2021 November」で検索すると、BTCに関する事件で逮捕されたなど記事が散見される。

www.justice.gov

「2016 bitfinex hack address」で調べると、逮捕前にBTCの送金(資金洗浄?)が行われていた旨の記事が見つかる。

https://cointelegraph.com/news/2-5b-in-stolen-btc-from-bitfinex-hack-awakens

このサイト内にトランザクションの詳細が記載されたツイートへのリンクがあり、Blockstream.infoへのリンクが見つかる。

それを参照し、取引しているアドレスを答えると正解だった。


解けなかった問題

Fireworks

8月の花火大会が開催されている場所をブルートフォースしようとしたら多過ぎたので撤退。

COMPANY

MSTがよく分からず放置してたら終わってた。

RIDE A BUS

あの画像で座標特定出来る人間本当に居るんですかね...

Otokichi

山本音吉の長男(John William Ottoson)が1928年8月に台北で亡くなった事以外何も分からず。

Google CTF 2021 writeup - filestore

哀れな一人チームで参戦。
1完出来ただけ良しとすべきかと思ったけどこの問題だけ321チームにも解かれていたので悩みどころ。
CPPは処理を追っている最中に眠くなったので断念。またwriteupを見て復習したい。

Filestore

問題概要

We stored our flag on this platform, but forgot to save the id. Can you help us restore it?

以下のコードが与えられる。

import os, secrets, string, time
from flag import flag


def main():
    # It's a tiny server...
    blob = bytearray(2**16)
    files = {}
    used = 0

    # Use deduplication to save space.
    def store(data):
        nonlocal used
        MINIMUM_BLOCK = 16
        MAXIMUM_BLOCK = 1024
        part_list = []
        while data:
            prefix = data[:MINIMUM_BLOCK]
            ind = -1
            bestlen, bestind = 0, -1
            while True:
                ind = blob.find(prefix, ind+1)
                if ind == -1: break
                length = len(os.path.commonprefix([data, bytes(blob[ind:ind+MAXIMUM_BLOCK])]))
                if length > bestlen:
                    bestlen, bestind = length, ind

            if bestind != -1:
                part, data = data[:bestlen], data[bestlen:]
                part_list.append((bestind, bestlen))
            else:
                part, data = data[:MINIMUM_BLOCK], data[MINIMUM_BLOCK:]
                blob[used:used+len(part)] = part
                part_list.append((used, len(part)))
                used += len(part)
                assert used <= len(blob)

        fid = "".join(secrets.choice(string.ascii_letters+string.digits) for i in range(16))
        files[fid] = part_list
        return fid

    def load(fid):
        data = []
        for ind, length in files[fid]:
            data.append(blob[ind:ind+length])
        return b"".join(data)

    print("Welcome to our file storage solution.")

    # Store the flag as one of the files.
    store(bytes(flag, "utf-8"))

    while True:
        print()
        print("Menu:")
        print("- load")
        print("- store")
        print("- status")
        print("- exit")
        choice = input().strip().lower()
        if choice == "load":
            print("Send me the file id...")
            fid = input().strip()
            data = load(fid)
            print(data.decode())
        elif choice == "store":
            print("Send me a line of data...")
            data = input().strip()
            fid = store(bytes(data, "utf-8"))
            print("Stored! Here's your file id:")
            print(fid)
        elif choice == "status":
            print("User: ctfplayer")
            print("Time: %s" % time.asctime())
            kb = used / 1024.0
            kb_all = len(blob) / 1024.0
            print("Quota: %0.3fkB/%0.3fkB" % (kb, kb_all))
            print("Files: %d" % len(files))
        elif choice == "exit":
            break
        else:
            print("Nope.")
            break

try:
    main()
except Exception:
    print("Nope.")
time.sleep(1)

考察

storeを選択し文字列を与えると、サーバー上の2**16のバイト列に書き込みが行われる。

「# Use deduplication to save space.」と書いてある部分に着目すると、文字列をstoreする際に先頭16バイトをサーバーに保存されている文字列と重複するかを確認した後、重複するプレフィックス長(最大1024バイト)を求めている。全て重複していなければ新たに保存し、16バイト分の文字列長がusedに加算される。

実験

フラグの形式は「CTF{...}」と分かっており、既にフラグが追加されているのもコードから確認出来るため、実際に「CTF{」という文字を送り付けてstatusを確認してみる。

== proof-of-work: disabled ==
Welcome to our file storage solution.

Menu:
- load
- store
- status
- exit
status
User: ctfplayer
Time: Fri Jul 23 02:54:44 2021
Quota: 0.026kB/64.000kB
Files: 1

Menu:
- load
- store
- status
- exit
store
Send me a line of data...
CTF{         
Stored! Here's your file id:
B4SoCXVzHwJUphrK

Menu:
- load
- store
- status
- exit
status
User: ctfplayer
Time: Fri Jul 23 02:54:54 2021
Quota: 0.026kB/64.000kB
Files: 2

Menu:
- load
- store
- status
- exit

Quotaに着目すると、「CTF{」の保存前と保存後で数値が変わっていない。
よって「CTF{」以降の文字をブルートフォースして数値が変わらないものがフラグの一部分とすればOK。

solver

サーバーでは最初の16バイトしか正しく重複検査できない。
例えば「CTF{hogehogeabcdefg}」というフラグが保存されていた場合、「CTF{hogehogeabcd」までしか期待した重複検査を行わず、「CTF{hogehogeabcda」という文字列を与えても数値が一致してしまう。

開催時は愚直に15文字以上になったら最初に指定するフラグの位置を変えてエスパーしたが、特定したフラグが15文字以上になった場合一文字ずらして送り付けるようにすれば綺麗に求まるので、他の方のwriteupを参考にしつつ書き直した。(とはいえ自分の環境ではタイムアウトが起きたのでどちらにせよ指定するフラグはズラさなければダメだった...。)

from pwn import *
import string

s = string.printable
quota = "0.026kB/64.000kB\n"
flag = "CTF{"
io = remote('filestore.2021.ctfcompetition.com',1337)
ctr = 0

while("}" not in flag):

    print(flag)

    for i in range(len(s)):
        flag_send = flag[ctr::] + s[i] 
        io.sendlineafter("- exit","store")
        io.sendlineafter("Send me a line of data...",flag_send)
        io.sendlineafter("- exit","status")
        io.recvline()
        io.recvline()
        io.recvline()
        quota_server = io.recvline()
        if(quota == quota_server.decode().replace("Quota: ","")):
            flag += s[i]
            break
        else:
            quota = quota_server.decode().replace("Quota: ","")
            #print(quota)

    if(len(flag) >= 15):
        ctr += 1

print("flag : %s" % flag)

CTF{CR1M3_0f_d3dup1ic4ti0n}

OS自作入門の環境構築メモ

OS自作入門本を買った。

主に自分が詰まったりした部分をまとめていきます。恐らく随時更新。

環境

WSLの有効化

f:id:Cy4n0s:20210713082900p:plain

なんで無効にしてたかすら不明。当然仮想プラットフォームを有効化してやる。

コントロールパネル -> プログラム -> プログラムと機能 -> Windowsの機能の有効化または無効化

ウィンドウを開いたら「仮想マシンプラットフォーム」にチェック。「Windowsハイパーバイザープラットフォーム」にもチェックを入れておくとVirtualBox(6.1.16以降)などと共存できるので吉。

disk.imgのマウントができない

day01において「sudo mount -o loop disk.img mnt」を実行すると「mount: ./mnt: mount failed: Operation not permitted.」というエラーが出る。自分の環境の場合、WSL1のまま動かしていたので2に変えてやる。

コマンドプロンプトPowerShellで「wsl --set-version Ubuntu-20.04 2」を実行すれば勝手に更新されるが、自分の場合カーネルコンポーネントの更新をサボっていたためにエラーが発生した。更新後再度実行したら無事に終了。下記サイトにエラーの詳細と対処が細かく載っている。

WSL バージョンを変更する方法 | SEECK.JP サポート

QEMU環境で実行時に「Could not connect: Connection refused」が発生する

原理をまだ理解出来ていないが、WSL1とWSL2でネットワーク周りの仕様が変更された事が原因らしい。この場合DISPLAY環境変数を変えてやることで対処可能。

「~/.profile」内の「export DISPLAY=:0.0」を「export DISPLAY=\$(cat /etc/resolv.conf | grep nameserver | awk '{print \$2}'):0.0」に変えて保存。

f:id:Cy4n0s:20210713095749p:plain

その後、VcXsrvを起動する際にExtra Settingsにて「-ac」をAdditional parametersに追記してやる。どうやらパブリックアクセスを許可するためのパラメータらしい。

f:id:Cy4n0s:20210713100021p:plain

参考↓

WSL2におけるVcXsrvの設定 - Qiita

UEFIアプリケーション開発環境を Windowsの WSL環境で構築して QEMU環境で動作確認する方法 (Windowsの起動画面のロゴ画像をカスタマイズする HackBGRTを PNGと JPEGファイル対応に改造する)

パケットからマルウェアを探る(Malware Traffic Analysis - ICEMAIDEN)

この記事は、 大阪工業大学 Advent Calendar 2020の22日目の記事です。

adventar.org

※悪用厳禁。何かしら問題が発生した場合は非公開にする可能性があります。

はじめに

20日目の記事でも挨拶をしましたが、初めましての方は初めまして。しあのすと申します。

今回は最近勉強したパケット解析についての備忘録を兼ねてまとめたいと思います。

マルウェア解析

マルウェア解析の流れとして「表層解析」、「動的解析」、「静的解析」の3段階が存在し、この順番に行われることが定石となっています。

セキュリティとパケット解析

ネットワーク障害の特定や設計段階の調査のために行われるイメージがありそうなパケット解析ですが、セキュリティ対応としてパケット解析が行われる場合も多いのが現状です。

俗に言うセキュリティアナリストやDFIR担当者が不正侵入防止の調査、またはマルウェアや攻撃者の不正侵入後の原因特定をするためにパケット解析が用いられます。

セキュリティのためのパケット解析はネットワーク設計などの調査とは異なり、攻撃者が制御している機器などのこちらから特定が困難な情報が関わってくるため、限られた情報から全貌を明らかにする必要があります。

Malware Traffic Analysis

実際にマルウェアとの通信をキャプチャした有害なpcapファイル検体の公開及びその解析演習を行うことが出来る Malware-Traffic-Analysis.netというサイトが存在します。

サイトには解析のための基本的なWiresharkチュートリアルやセキュリティのためのパケット解析のトピックなども載っており、Exercisesには各問模範解答が付属しているため、興味を持った方は一度自力で考えてみても面白いかもしれません。

今回はこのサイトのICEMAIDENを例にパケットから調査を行っていきます。

解析

※解析の際は仮想環境上で外部通信やホストとの共有ファイルを切ったことを確認した上で行うことをおすすめします。解析の途中で出てきた有害なIPやURLは意図的にエスケープしています。この記事を見た後に実際にアクセスして何か被害が生じた場合、一切責任を負いません。

シナリオ
LANセグメントデータ:

LANセグメント範囲:10.18.20.0/24(10.18.20.0〜10.18.20.255)
ドメイン:icemaiden.com
ドメインコントローラー:10.18.20.8-Icemaiden-DC
LANセグメントゲートウェイ:10.18.20.1
LANセグメントブロードキャストアドレス:10.18.20.255
あなたのタスク
pcapとアラートを確認してから、次の質問に答えてください。

問1 : 感染したWindowsホストのIPアドレス、MACアドレス、およびホスト名は何ですか?
問2 : この感染したWindowsホストの被害者のWindowsユーザーアカウント名は何ですか?
問3 : 被害者はどのような種類のマルウェアに感染しましたか?
問4 : pcapからのトラフィックに基づいて、マルウェアはどこから来た可能性がありますか?
問5 : 最初の感染後、被害者はどのタイプのWebページ/ Webサイトにアクセスしたように見えましたか?

問1

pcapと共に与えられるアラートの画像に注目すると、「TROJAN Ursnif」の文字が見つかるのでこれに着目します。

f:id:Cy4n0s:20201222212900p:plain
アラート

送信先ポートが80番であることからHTTPリクエストと10.18.20.97でフィルターをかけて検索してみます。

f:id:Cy4n0s:20201222214040p:plain

10. 18. 20. 97から8. 208. 24. 139へのパケットが大量に見つかるため、これがアラートにひっかかったと推測出来ます。よってIPは「10.18.20.97」。

MACアドレスはパケット詳細欄のEthernet IIの欄から特定できます。

f:id:Cy4n0s:20201222214511p:plain

Source: Acer_56:9b:cf(00:01:24:56:9b:cf)とあることから、MACアドレスは「00:01:24:56:9b:cf」。

ホスト名はkerberosの通信から特定します。kerberosのCNAMEレコードにはクライアントの主要な識別子の名前が含まれているので、kerberos.CNameString and ip.addr==10.18.20.97と入力してフィルターをかけてみます。

f:id:Cy4n0s:20201222215153p:plain

CNameStringの値から、ホスト名は「JUANITA-WORK-PC」。

問2

先程の結果のCNameStringの欄で右クリックし、「列として適用(Apply as Column)」を押すと、CNameStringの結果が列として表示されます。

f:id:Cy4n0s:20201222220012p:plain

ここで、ユーザーアカウント名はホスト名と違い「$」で終わらないという特性を用いると、ユーザーアカウント名は「momia.juanita」と特定できます。

問3

問1でUrsnifのアラートが出ていることから、マルウェアの種類は「Ursnif」だと考えられます。

さらに、 Wiresharkによるパケット解析講座 6: Ursnif感染の調査を参照すると、http.requestのフィルターをかけた際に上で言及されている特殊なGETやPOSTリクエストが散見されることも分かります。このような根拠からもUrsnifと推測して良いと考えられます。

f:id:Cy4n0s:20201222220823p:plain

問4

Ursnifで一度Web検索をかけてみます。

www.trendmicro.com

上の記事を参照すると、Ursnifは不正なプログラムによって他のサイトからダウンロードされるか、不正なメールから侵入する場合が殆どだと分かります。

http.requestでフィルターをかけてみると、一番最初にhttp:// mail[.]aol[.]com/というメールサイトにアクセスしているのが分かります。 また、このメールサイトにアクセスしているのが不審なGET/POSTリクエストが送られる前であることから、(上記URL)において、不正なメールから感染したと考えて良いと思われます。 f:id:Cy4n0s:20201222221354p:plain

問5

http・https通信に絞るため、(http.request or ssl.handshake) and !ssdpというフィルターをかけます。

15228~16215,16315,16436~16493において不審なGET/POSTリクエストが確認された後のパケットを見てみると、~[.]bac[.]combankofamericaの文字が散見されます。

f:id:Cy4n0s:20201222224928p:plain

bac[.]comwhoisをかけるとAdminの情報辺りでやはりbankofamericaの文字が見つかるため、bankofamerica[.]comにアクセスしたと推測出来ます。

Ursnifがバンキングマルウェアとして有名なことからの関連でしょうか。

f:id:Cy4n0s:20201222224106p:plain

おわりに

HTTP通信から不審なプログラムをエクスポートしてVirusTotalで検索をかけたり、送信されている暗号化データを復号出来たりするとさらに情報を抜き出すことが可能でしょうが、今の技術力や記事を書く時間的にこの辺りが限界でした。

解析の根拠にも間違いや誤魔化しが多い可能性があるので精進します。

この記事によって少しでもパケット解析やフォレンジックに興味を持って頂ければ幸いです。

WaniCTF writeup(Crypto,Forensics)


f:id:Cy4n0s:20201124151556j:plain

25問解いて187人中24位でした。

ForensicsとCryptoに加えてMiscも全完出来たので嬉しかったです。

RevとPwnは精進します。

Crypto

Veni, vidi

SYNT{fvzcyr_pynffvpny_pvcure}

ROT13で復号する。

FLAG{simple_classical_cipher}

exclusive

XORを使った暗号です🔐

encrypt.pyとoutput.pyが与えられる。

key = "REDACTED"
flag = "FAKE{this_is_fake_flag}"

assert len(key) == len(flag) == 57
assert flag.startswith("FLAG{") and flag.endswith("}")
assert key[0:3] * 19 == key


def encrypt(s1, s2):
    assert len(s1) == len(s2)

    result = ""
    for c1, c2 in zip(s1, s2):
        result += chr(ord(c1) ^ ord(c2))
    return result


ciphertext = encrypt(flag, key)
print(ciphertext, end="")

コード中の assert key[0:3] * 19 == keyから鍵が3文字と分かり、フラグの形式がFLAG{で始まるので既知平文攻撃が出来る。

with open('output.txt') as c:
    strings = c.read()

known = 'FLAG{'

known = known[0:5]*len(strings)
key=[]
f=[]

for c1,c2 in zip(strings,known):
    key.append(chr(ord(c1)^ord(c2)))

key = key[0:3]*19

for c,m in zip(strings,key):
    f.append(chr(ord(c)^ord(m)))

flag = ''.join(f)

print(flag)

FLAG{xor_c1ph3r_is_vulnera6le_70_kn0wn_plain7ext_@ttack!}

Basic RSA

RSA暗号の基本的な演算ができますか?

接続するとRSAに関する計算を求められる。

p : 素数1 , q : 素数2 , N : 公開鍵1(p*q) , e : 公開鍵2 , m : 平文 , c : 暗号文

として、

・p,qからN

・m,e,nからc

・p,q,e,cからm

の計算を行って順番に投げつけるとフラグが返ってくる。

最後のp,q,e,cからmを導出するには

phi = (p-1)*(q-1)

d = e^-1 (mod phi)

が必要なので適宜計算する。dに関してはpow(e,-1,phi)で出せば早い。

ターミナルからPythonを起動して計算したのでコードは残ってません。

FLAG{y0uv3_und3rst00d_t3xtb00k_RSA}

LCG crack

安全な暗号は安全な乱数から

server.pyが配布される。

import random
import os

from Crypto.Util.number import *

from const import flag, logo, description, menu


class RNG:
    def __init__(self, seed, a, b, m):
        self.a = a
        self.b = b
        self.m = m
        self.x = seed % m

    def next(self):
        self.x = (self.a * self.x + self.b) % self.m
        return self.x


def show_menu():
    print(menu)
    log(description)
    while not (choice := input("> ")) in "123":
        print("Invalid choice.")

    return int(choice)


if __name__ == "__main__":
    print(logo)
    seed = random.getrandbits(64)
    a = random.getrandbits(64)
    b = random.getrandbits(64)
    m = getPrime(64, os.urandom)
    rng = RNG(seed, a, b, m)

    while True:
        choice = show_menu()

        # Print
        if choice == 1:
            print(rng.next())

        # Guess
        elif choice == 2:
            for cnt in range(1, 11):
                print(f"[{cnt}/10] Guess the next number!")
                try:
                    guess = int(input("> "))
                except ValueError:
                    print("Please enter an integer\n\n\n")
                    continue
                if guess == rng.next():
                    print(f"Correct! ")
                    cnt += 1
                else:
                    print(f"Wrong... Try again!")
                    break
            else:
                print(f"Congratz!  {flag}")
                break

        # Exit
        else:
            print("Bye :)")
            break

線形合同法

choiceが1の時に次の乱数を取得、2の時に次の乱数を当てるモードになる。

seed,a,b,mが固定されてそうなので逆算すればいけるかと予想したら実装に困ってしまった...。

色々調べてたら次の記事を見つけた。

satto.hatenadiary.com

今回のケースは記事の「A (multiplier) と B ( increment) と M (modulus) が未知である場合」と同じ。

記事からコードを引用させていただきソルバを書いた。

#https://satto.hatenadiary.com/entry/solve-LCG

from Crypto.Util.number import inverse, GCD, getRandomNBitInteger
from functools import reduce
from pwn import *

def solve_unknown_modulus(states):
    diffs = [X_1 - X_0 for X_0, X_1 in zip(states, states[1:])]
    multiples_of_M = [T_2 * T_0 - T_1 ** 2 for T_0, T_1, T_2, in zip(diffs, diffs[1:], diffs[2:])]

    M = reduce(GCD, multiples_of_M)
    return M

def solve_unknown_multiplier(states, M):
    A = (states[2] - states[1]) * inverse((states[1] - states[0]), M)
    return A

def solve_unknown_increment(states, A, M):
    B = (states[1] - A * states[0]) % M
    return B

state = []
io = remote('lcg.wanictf.org',50001)
i=0
while(i<30):
    io.sendlineafter('> ',"1")
    app = int(io.readline(),10)
    print(app)
    state.append(app)
    i+=1

print('------------------------')


M = solve_unknown_modulus(state)
A = solve_unknown_multiplier(state, M)
B = solve_unknown_increment(state, A, M)

next_value = (A * state[-1] + B) % M
print(next_value)
io.sendlineafter('> ',"2")
io.sendlineafter('> ',str(next_value))
print(io.recvline())

for j in range(9):
    next_value = (A * next_value + B) % M
    print(next_value)
    io.sendlineafter('> ',str(next_value))
    print(io.recvline())

print(io.recvline())

FLAG{y0u_sh0uld_buy_l0tt3ry_t1ck3ts}

l0g0n

🕵️‍♂️

server.pyが与えられる。

from hashlib import pbkdf2_hmac
import os

from Crypto.Cipher import AES

from secret import flag, psk


class AES_CFB8:
    def __init__(self, key):
        self.block_size = 16
        self.cipher = AES.new(key, AES.MODE_ECB)

    def encrypt(self, plaintext: bytes, iv=bytes(16)):
        iv_plaintext = iv + plaintext
        ciphertext = bytearray()

        for i in range(len(plaintext)):
            X = self.cipher.encrypt(iv_plaintext[i : i + self.block_size])[0]
            Y = plaintext[i]
            ciphertext.append(X ^ Y)
        return bytes(ciphertext)


def key_derivation_function(x):
    dk = pbkdf2_hmac("sha256", x, os.urandom(16), 100000)
    return dk


def main():
    while True:
        client_challenge = input("Challenge (hex) > ")
        client_challenge = bytes.fromhex(client_challenge)

        server_challenge = os.urandom(8)
        print(f"Server challenge: {server_challenge.hex()}")

        session_key = key_derivation_function(psk + client_challenge + server_challenge)

        client_credential = input("Credential (hex) > ")
        client_credential = bytes.fromhex(client_credential)

        cipher = AES_CFB8(session_key)
        server_credential = cipher.encrypt(client_challenge)
        if client_credential == server_credential:
            print(f"OK! {flag}")
        else:
            print("Authentication Failed... 🥺")


if __name__ == "__main__":
    main()

AES_CFB8と問題名の「l0g0n」からZerologonを思い出す。

www.youtube.com

動画によると、AES_CFB8ではivを0で固定すると暗号文の先頭バイトが256分の1の確率で0となる。

また、0を暗号化すると0になるため、256回の鍵生成したうち1回は平文と同じ0となる。

ChallengeとCredentialを暗号化した結果を比較して同じであればフラグが取得出来るので、上記の脆弱性にしたがって8Byte分の0をループで送り続けるというソルバを書いた。

from pwn import *

io = remote('l0g0n.wanictf.org',50002)
while(True):
    io.sendlineafter('Challenge (hex) > ','0000000000000000')
    io.sendlineafter('Credential (hex) >','0000000000000000')
    rcv = io.recvline() 
    if(b'OK!' in rcv):
        print(rcv)
        break
    print('not')

FLAG{4_b@d_IV_leads_t0_CVSS_10.0__z3r01090n}

Forensics

logged_flag

ワニ博士が問題を作っていたので、作っているところをキーロガーで勝手に記録してみました。
先に公開してしまいたいと思います。
(ワニ博士は英字配列のキーボードを使っています)

key_log.txtとsecret.jpgが渡される。

key_log.txtを見る限りechoでフラグをflag.txtに書き込んでそれをsteghideでjpgファイルに埋め込んでいる。

echoのコマンド文がそのまま見れるので頑張ってフラグを取り出すか、「steghide embed」で打ち込んでるパスワードmachikanetamachikanesaiを抜き出して、「steghide extract -sf test.jpg」からパスワードを入力してflag.txtを抜き出す。

11:50:15 [E]
11:50:15 [C]
11:50:15 [H]
11:50:15 [O]
11:50:16 [Space]
11:50:23 [Shift]
11:50:24 [F]
11:50:26 [Shift]
11:50:26 [L]
11:50:27 [Shift]
11:50:27 [A]
11:50:28 [Shift]
11:50:28 [G]
11:50:29 [Shift]
11:50:29 [[]
11:50:31 [K]
11:50:32 [3]
11:50:33 [Y]
11:50:35 [Shift]
11:50:35 [-]
11:50:36 [L]
11:50:36 [0]
11:50:37 [G]
11:50:38 [G]
11:50:38 [3]
11:50:38 [R]
11:50:41 [Shift]
11:50:41 [-]
11:50:41 [1]
11:50:42 [S]
11:50:43 [Shift]
11:50:43 [-]
11:50:44 [V]
11:50:45 [3]
11:50:47 [R]
11:50:47 [Y]
11:50:47 [Shift]
11:50:47 [-]
11:50:49 [D]
11:50:49 [4]
11:50:50 [N]
11:50:51 [G]
11:50:52 [3]
11:50:52 [R]
11:50:53 [0]
11:50:54 [U]
11:50:54 [S]
11:50:55 [Shift]
11:50:55 []]

FLAG{k3y_l0gg3r_1s_v3ry_d4ng3r0us}

chunk_eater

pngの必須チャンクをワニ博士が食べてしまいました!

eaten.pngPNGフォーマットの解説サイトが渡される。

eaten.jpgをバイナリエディタで開くとPNGの必須チャンクと思われるIHDR IDAT IENDが全てWANIに書き換わっているので、これらを全て元に戻してやる。最初と最後以外のWANIは全てIDATに書き換えた。

正確に修復出来るとフラグの画像が出てくる。

f:id:Cy4n0s:20201124182641p:plain

FLAG{chunk_is_so_yummy!}

zero_size_png

この画像のサイズは本当に0×0ですか?

dyson.pngとIHDRの解説サイトが渡される。

バイナリエディタで開くと画像の幅(赤)と画像の高さ(青)が0になっている。

ただChunk Type(茶)とCRC(緑)は元から存在している。

f:id:Cy4n0s:20201124184155p:plain

CRCはChunk Typeと幅,高さから導出出来るので、CRCから幅と高さを逆算するっぽいな~と思い色々調べてたら見つけた。

www.programmersought.com

zlibのcrc32を使えばxorの実装せずに計算してくれるので楽っぽい。

上記のサイトのコードを引用させていただき幅と高さを求める。

#https://www.programmersought.com/article/80384546662/

import zlib
import struct

filename = 'dyson.png'
with open(filename, 'rb') as f:
    all_b = f.read()
    crc32key = int(all_b[29:33].hex(),16)
    data = bytearray(all_b[12:29])
    n = 4095
    for w in range(n):
        width = bytearray(struct.pack('>i', w))
        for h in range(n):
            height = bytearray(struct.pack('>i', h))
            for x in range(4):
                data[x+4] = width[x]
                data[x+8] = height[x]
            crc32result = zlib.crc32(data)
            if crc32result == crc32key:
                print("Wide is:",end="")
                print(width.hex())
                print("High as:",end="")
                print(height.hex())
                exit(0)
Wide is:00000257

High as:0000030d

後は出力通りに幅と高さを書き換える。

f:id:Cy4n0s:20201125054014p:plain

f:id:Cy4n0s:20201125054110p:plain

FLAG{Cyclic_Redundancy_CAT}

ALLIGATOR_01

ワニ博士のPCでは,悪意のあるプロセスが実行されているみたいです。

取得したメモリダンプから、”evil.exe”が実行された日時を報告してください。

(注意: スペースはすべて半角のアンダースコアにしてください)

example: FLAG{1234-56-78_99:99:99_UTC+0000}

Memory Forensics三部作。

ALLIGATOR.zipを展開するとALLIGATOR.rawが出てくる。

Volatilityが推奨ツールとして紹介されていたので導入した。

使い方等は下記サイトを参照した。

jpn.nec.com

troushoo.blog.fc2.com

初動調査としてOSのプロファイルを調べる必要があるので下記コマンドで調べる。

volatility -f ALLIGATOR.raw imageinfo

Volatility Foundation Volatility Framework 2.6
INFO    : volatility.debug    : Determining profile based on KDBG search...
          Suggested Profile(s) : Win7SP1x86_23418, Win7SP0x86, Win7SP1x86_24000, Win7SP1x86
                     AS Layer1 : IA32PagedMemoryPae (Kernel AS)
                     AS Layer2 : FileAddressSpace (/root/Documents/wanictf/for/alligator/ALLIGATOR.raw)
                      PAE type : PAE
                           DTB : 0x185000L
                          KDBG : 0x82754de8L
          Number of Processors : 1
     Image Type (Service Pack) : 1
                KPCR for CPU 0 : 0x80b96000L
             KUSER_SHARED_DATA : 0xffdf0000L
           Image date and time : 2020-10-26 03:04:49 UTC+0000
     Image local date and time : 2020-10-25 20:04:49 -0700

Win7SP0x86で良さそう。pstreeでメモリダンプ取得時に動いていたプロセスが確認出来る。

volatility -f ALLIGATOR.raw --profile=Win7SP0x86 pstree

省略
. 0x84dd6b28:evil.exe                                3632   2964      1     21 2020-10-26 03:01:55 UTC+0000
省略

日時が載っているのでこれをフラグ形式にしたらフラグ完成。

FLAG{2020-10-26_03:01:55_UTC+0000}

ALLIGATOR_02

コマンドプロンプトの実行履歴からFLAGを見つけてください。

(ALLIGATOR_01で配布されているファイルを使ってください)

consolesでcmd.exe で実行されたコマンドの出力を表示出来るらしい。

volatility -f ALLIGATOR.raw --profile=Win7SP0x86 consoles

省略

C:\Users\ALLIGATOR>type C:\Users\ALLIGATOR\Desktop\flag.txt                     
FLAG{y0u_4re_c0n50les_master}                                                   
C:\Users\ALLIGATOR> 

FLAG{y0u_4re_c0n50les_master}

ALLIGATOR_03

Dr.WANIはいつも同じパスワードを使うらしいです。

Dr.WANIのパソコンから入手したパス付のzipファイルを開けて、博士の秘密を暴いてしまいましょう。

(ALLIGATOR_01で配布されているファイルを使ってください)

hashdumpでメモリ上のパスワードハッシュをダンプできる。

SYSTEMとSAMのアドレスがなくてもALLIGATORのパスワードハッシュが出てきたけど本来ならば指定するっぽい。

volatility -f ALLIGATOR.raw --profile=Win7SP0x86 hashdump

Administrator:500:aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
IEUser:1000:aad3b435b51404eeaad3b435b51404ee:fc525c9683e8fe067095ba2ddc971889:::
sshd:1001:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
sshd_server:1002:aad3b435b51404eeaad3b435b51404ee:8d0a16cfc061c3359db455d00ec27035:::
ALLIGATOR:1003:aad3b435b51404eeaad3b435b51404ee:5e7a211fee4f7249f9db23e4a07d7590:::

5e7a211fee4f7249f9db23e4a07d7590をhashes.txtに移したらJohn the Ripperなどで復号する。

john hashes.txt --wordlist=/usr/share/wordlists/rockyou.txt --format=NT

Loaded 1 password hash (NT [MD4 256/256 AVX2 8x3])
Warning: no OpenMP support for this hash type, consider --fork=4
Press 'q' or Ctrl-C to abort, almost any other key for status
ilovewani        (?)
1g 0:00:00:00 DONE (2020-11-25 06:35) 4.166g/s 7149Kp/s 7149Kc/s 7149KC/s iloveyoh2..iloveus5
Use the "--show --format=NT" options to display all of the cracked passwords reliably
Session completed

ilovewaniをパスワードとしてwani_secret.zipを解凍するとflag.txtが出てきた。

大阪大学 公式マスコットキャラクター「ワニ博士」
【プロフィール】
名前:   ワニ博士(わにはかせ)
誕生日:    5 月 3 日
性別:   オス
出身地:    大阪府 豊中市 待兼山町
【性格】
温厚,好奇心旺盛,努力型,お茶目,社交的,たまに天然,賢い
【趣味】
・阪大キャンパスでコーヒーを飲みながら学生としゃべる
・粉もん屋めぐり
・化石集め。(いつか自分の仲間に会うために)
・CTF: FLAG{The_Machikane_Crocodylidae}

FLAG{The_Machikane_Crocodylidae}

USBドライブにTailsを入れる

タイトル通り。スクショも面倒だったのであまり撮ってません。 入れる前にUSBドライブがフォーマット済か要確認。

① : isoとブート用USB作成ツールを拾ってくる

Tails - Install from Windows

balenaEtcher - Flash OS images to SD cards & USB drives

② : USBにTailsを入れる

Etcher-Portableを起動して、"Flash from file"にisoファイル、Select TargetにUSBドライブを指定。 後は"Flash!"を押して待つだけ。 f:id:Cy4n0s:20201104044229p:plain f:id:Cy4n0s:20201104044242p:plain

③ : BIOS設定

セキュアブートを切ってUSBドライブの優先順位や許可をよしなにしておく。 SurfaceだとUSBブートは最初からONになってた。

後はUSBから起動するだけ。 後は永続化ボリューム作るなり好きにする。(セキュリティ的にはよろしくない)

参考 : Tails - Creating and configuring the Persistent Storage