はじめに

概要

前回から一年以上経って久々のAlpacaHackです。 どうやら毎日問題が出題されるDaily AlpacaHackというものが始まっていて、 しかも初心者向けの教育的な問題ということなので、 CTF初心者の私にちょうどよさそうです。 今回は直近で簡単そうな問題(Easy)である2026年5月8日の「Vending Machine」に挑戦してみようと思います。

Daily AlpacaHack 2026/05/08 - Vending Machine

AlpacaHack

AlpacaHackへのリンクはこちら

AlpacaHackのサイトへのリンク

前回の記事

2024年12月09日の記事です。

AlpacaHackで始めるCTF入門7:AlpacaHack Round 5 - XorshiftStreamに挑戦

問題内容

今回挑戦する問題「Vending Machine」は自販機スクリプトからFlagを購入することを目指す問題のようです。 スクリプトを実行すると入力が求められるのでaeのいずれかを入力するとそれに対応した商品が購入できます。 ここでfがFlagに対応しているのですが、単純にfを入力しても不正な入力と判定されてしまいます。

実際に実行してみた↓

$ nc 34.170.146.252 30573
Please select an item.
----------------
a: apple juice
b: banana juice
c: coke
d: draft beer
e: energy drink
f: flag
x: exit
----------------
your choice> a  # aを入力
You bought apple juice.
Thank you!
your choice> f  # fを入力
Invalid choice.
your choice> x  # xを入力
Bye.

スクリプトを読んでみる

この問題の自販機スクリプトserver.pyを読んでみます。

上から見ていくと商品のストックが文字列として用意されています。

self.stock = 'a'*30 + 'b'*60 + 'c'*20 + 'd'*50 + 'e'*40 + 'f' # 'aaa...eeef'

この文字列を見た時、連続して購入して商品のストックがなくなったらどうなるのか気になりました。 ということでbuy()を確認してみます。下記が一部抜粋です。

loc = self.stock.find(mark)  # 入力した文字を文字列内から探す
stock_list = list(self.stock)
item = stock_list.pop(loc)  # 見つけた文字を配列から取り出す
self.stock = ''.join(stock_list)
name = self.item_names[item]
print(f"You bought {name}.")
if item == 'f':  # itemがfだったらFlagが出力される
    print(f"Flag:", FLAG)
else:
    print("Thank you!")

buy()を読んでみると、シンプルに入力された文字を文字列から探して取り出す処理が行われています。 そして取り出した文字に対応した商品を出力するのですが、ここでfが取り出されたらFlagを取得できます。

解法

たとえばcの商品を20個購入してストックの文字列からcがなくなった状態でさらにcを購入した場合どうなるかについて考えてみます。 ストックの文字列内にcがない状態だとfind()-1を返します。 そしてその後pop()-1を指定して文字を取り出すことになるので文字列の末尾の文字が取り出されます。 ということはストックの文字列の末尾はfなので、これでFlagが取得できます。

おわりに

今回はDaily AlpacaHackの2026年5月8日の問題「Vending Machine」に挑戦してみました。 レベルがEasyということで簡単な問題だったとは思いますが、 おそらくAlpacaHackで自力で解けた初めての問題だったのでとても嬉しいです(チュートリアル的な問題を除く)。 デイリー問題は初心者の私でもとっつきやすそうな問題っぽいのでまた挑戦してみようと思います。 それでは、また。