LoginSignup
1
0

osu!gaming CTF 2024 Writeup

Posted at

osu!gaming CTF 2024

順位は367位/984位でした。

score.png

目次

  • misc/discord
  • misc/survey
  • osu/sanity-check-1
  • misc/Infiltrating the OSU! (I)
  • crypto/base727
  • web/mikufanpage
  • crypto/ROSSAU
  • osu/smoked
  • pwn/betterthanu
  • misc/Infiltrating the OSU! (II)
  • forensics/nathan-on-osu
  • まとめ

misc/discord

チャンネル「announcements」で開始直後(2024/3/2 2:00)にsahuangさんが発言した文章の中にflagがあります。
文章がかなり長いので今回は写真省略します。

osu{Rhy7hm_15_L1f3}

misc/survey

アンケートに答えるとflagが取得できます。

osu{push_these_poochs_off_me_like_BAU}

osu/sanity-check-1

ビートマップ情報のタグを見るとflagが書いてあります。
sanity-check-1.png

osu{welc0me_2_osu!!}

misc/Infiltrating the OSU! (I)

この問題は以下の画像のURLを「https://assets.ppy.sh/」の中から探し出し送信するとflagが送信されます。
osu-banner.jpg

ヒントとしてhoifanrdさんのosu!プレイヤー情報が提供されているのでその中から同じような画像がないか調べます。

Infiltrating_the_OSU_1.png

赤い矢印で指された画像のURLを送ればよさそうです。この画像のURLは開発者ツールで見ることができます。
ちなみにここでCtrl+Shift+Cを同時押しするとその画像をクリックするだけで画像のURLがわかるのでお勧めです。

送信したらflagが出てきました。

osu{tr1v4l_5rc_1n5p3c7_1n_webs1t3}

crypto/base727

カテゴリにChatGPTと書いてあったので与えられたファイルのコード(727.py)の逆の手順をたどるコードをChatGPTに依頼したらこのようなコードが返ってきました。

import binascii

def decode_base_727(encoded_string):
    base = 727
    decoded_value = 0

    for char in encoded_string:
        decoded_value = decoded_value * base + ord(char)

    decoded_string = ""
    while decoded_value > 0:
        decoded_string = chr(decoded_value % 256) + decoded_string
        decoded_value //= 256

    return decoded_string

# 与えられたBase727でエンコードするコード
encoded_hex_string = b'06c3abc49dc4b443ca9d65c8b0c386c4b0c99fc798c2bdc5bccb94c68c37c296ca9ac29ac790c4af7bc585c59d'
encoded_string = binascii.unhexlify(encoded_hex_string).decode()

decoded_string = decode_base_727(encoded_string)
print(decoded_string)

実行するとflagが出ました。

727.png

osu{wysiwysiwysiywsywiwywsi}

web/mikufanpage

ダウンロードしたファイルの中にReadme.mdがあったので開いたらflagがあったのでそのまま入力したら正解してしまった・・・
大丈夫なのかこれ。

ちなみに正攻法は
https://mikufanpage.web.osugaming.lol/image?path=miku1.jpg./../flag.txtのように接続するとflagが出てくるディレクトリトラバーサルの問題だったらしいです。

osu{miku_miku_miku_miku_miku_miku_miku_miku_miku_miku_miku_miku_miku}

crypto/ROSSAU

これはeがかなりでかいのでwiener's attackが適用できます。
過去にwiener's attackの記事書いているみたいなのでもしよかったらどうぞ。

今回はdが分かればいいのでこのコードで行きます。

import owiener

e = 876603837240112836821145245971528442417
n = 5912718291679762008847883587848216166109
d = owiener.attack(e, n)

if d is None:
    print("Failed")
else:
    print("d={}".format(d))

以下のように求まりましたがこれが答えではありません。ユーザ名を特定する必要があります。

ROSSAU_d.png

osu!のプレイヤー情報のURLのところを見てみると数字が入っているので数字をdの値に変更したプレイヤー情報にしてアクセスすると・・・

ROSSAU_user.png

chocomintさんのプレイヤー情報になりました。したがってchocomintさんが正解です。

osu{chocomint}

osu/smoked

与えられたファイルはosrファイルでosu!のゲームのリプレイファイルとなっています。

osu!アカウントを持っていなくてもこちらでできます。

flagはリプレイしている最中にカーソルで書いているみたいです。ちなみに私はosu!をやったことないのでもしかしたらカーソルじゃなくてほかの方法でやってる可能性があります。

osu{smoked_map_fun}

pwn/betterthanu

コードは以下のようになっています。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>

FILE *flag_file;
char flag[100];

int main(void) {
    unsigned int pp;
    unsigned long my_pp;
    char buf[16];

    setbuf(stdin, NULL);
    setbuf(stdout, NULL);

    printf("How much pp did you get? ");
    fgets(buf, 100, stdin);
    pp = atoi(buf);

    my_pp = pp + 1;

    printf("Any last words?\n");
    fgets(buf, 100, stdin);

    if (pp <= my_pp) {
        printf("Ha! I got %d\n", my_pp);
        printf("Maybe you'll beat me next time\n");
    } else {
        printf("What??? how did you beat me??\n");
        printf("Hmm... I'll consider giving you the flag\n");

        if (pp == 727) {
            printf("Wait, you got %d pp?\n", pp);
            printf("You can't possibly be an NPC! Here, have the flag: ");

            flag_file = fopen("flag.txt", "r");
            fgets(flag, sizeof(flag), flag_file);
            printf("%s\n", flag);
        } else {
            printf("Just kidding!\n");
        }
    }

    return 0;
}

以下の条件を満たせばflagを取得することができます。

  • pp = 727であること(最初に入力する部分)
  • pp > my_pp であること(ただしmy_pp = pp + 1)

1つ目の条件を守ると2つ目は
my_pp = 728
pp = 727
なので条件を満たさなくなってしまいます。

2つ目の条件を守る(unsigned int型を利用してpp = 4294967295)と入力すると
pp = 727ではないので1つ目の条件を満たさなくなります。

ここで以下の部分に注目します。

    printf("Any last words?\n");
    fgets(buf, 100, stdin);

この部分にたくさんの文字を入れてif (pp <= my_pp)を無視できるように書き換えれば2つ目の条件を回避することができます。これで1つ目の条件を守りつつ2つ目の条件を回避でflagが手に入るようになります。

betterthanu.png

ちなみに私は9を16回入力して回避しました。

osu{i_cant_believe_i_saw_it}

実はpwnは外部CTFで初めて自力で解けました。

misc/Infiltrating the OSU! (II)(解けなかった)

Google画像検索するとその画像があるURLを調べられます。

Infiltrating_the_OSU_2.png

その中で「i.ppy」にあり、かつ寸法が1100x280pxの画像を探してURLを送信するとflagが送信されます。

osu{d1d_y0u_p2w???0r_d1d_y0u_f2w_by_u51ng_7h3_f0rum???}

ちなみにWriteup書いている間に分かりました。misc/Infiltrating the OSU! (I)の影響を強く受けすぎてhoifanrdさんのosu!プレイヤー情報のサイトのなかをずっと探し回ってました。英語ちゃんと読めていれば・・・

forensics/nathan-on-osu(解けなかった)

スクショなのでなんか加工前の画像に復元するものあるのかなと思っていたらやっぱりありました。
こちらは「aCropalypse」という脆弱性でherietさんが詳しく解説しています。

以下のサイトで高さ1080 x 幅1920と入力して問題の写真ファイルを選択するとできるみたいです。ちなみにどうやってその高さと幅を特定するかはわかりませんでした・・・

nathan-on-osu.png

osu{cr0pp3d_Future_Candy<3}

まとめ

後もう少しで解けた問題があったのでかなり悔しいです・・・

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0