基本情報リンクをコピーしました
- CTF名:Alpacahack Daily
- 開催日時:2026/03/09
- カテゴリ:Misc
- 問題URL:https://alpacahack.com/daily/challenges/flag-over
問題文リンクをコピーしました
シークレットが環境変数にあるなら上書きしちゃえば安全だぜ!
おっ、そうだな。
作業ログリンクをコピーしました
ファイルをダウンロードして展開。
flag-over/
├── Dockerfile
├── compose.yaml
└── server.py
1 directory, 3 filesとてもシンプルなプログラム。
FROM python:3.14.2
WORKDIR /app
RUN apt-get update && apt-get install -yq socat
COPY server.py ./
USER nobody:nogroup
CMD ["socat", "-T30", "tcp-listen:1337,fork,reuseaddr", "exec:'python server.py',stderr"]
services:
sandbox:
build: .
restart: unless-stopped
ports:
- ${PORT:-1337}:1337
environment:
- FLAG=Alpaca{REDACTED}
docker関連のファイルは特に問題なさそう。
FLAGという環境変数を仕込んであるからこれを奪取するのね。
本命のPythonコードを見てみよう。
import os
import subprocess
assert os.getenv("FLAG").startswith("Alpaca{")
subprocess.run(["bash", "-i"], env={"FLAG": "🦙"})
これまたシンプル。
まず環境変数FLAGがちゃんと登録されているかを確認してから、
subprocessでFLAGの中身を🦙に上書きしてスポーンさせ、ユーザーからの操作を受け付けてる。
実際にアクセスして確認してみよう。
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
nobody@XXXXX:/app$ echo $FLAG
🦙
nobody@XXXXX:/app$ env
PWD=/app
FLAG=🦙
SHLVL=1
_=/usr/bin/env
nobody@XXXXX:/app$プログラムの意図通りFLAGが書き換えられている。
さてどうハックするか。
…どうせどっかにファイルとして記されてるんだろ!
UNIX系列のOSにはEverything is a fileという思想があるので困ったらファイルを探して読めばいい。
環境変数に関するファイルってなんだっけな。調べてみよう。
どうやら/procっぽいな。/procはカーネルが今持っている情報をファイルっぽく見せる仮想ファイルシステムで、システム全体の情報、各プロセスごとの情報、実行中に変えられるカーネル設定が並んでいる。
total 4
dr-xr-xr-x 296 root root 0 Apr 16 04:35 .
drwxr-xr-x 1 root root 4096 Apr 16 04:35 ..
dr-xr-xr-x 9 nobody nogroup 0 Apr 16 04:35 1
dr-xr-xr-x 9 nobody nogroup 0 Apr 16 04:36 11
dr-xr-xr-x 9 nobody nogroup 0 Apr 16 04:36 12
dr-xr-xr-x 9 nobody nogroup 0 Apr 16 04:36 13
dr-xr-xr-x 9 nobody nogroup 0 Apr 16 04:36 14
drwxrwxrwt 2 root root 40 Apr 16 04:35 acpi
-r--r--r-- 1 root root 0 Apr 16 04:36 buddyinfo
dr-xr-xr-x 4 root root 0 Apr 16 04:35 bus
-r--r--r-- 1 root root 0 Apr 16 04:36 cgroups
-r--r--r-- 1 root root 0 Apr 16 04:36 cmdline
-r--r--r-- 1 root root 0 Apr 16 04:36 consoles
-r--r--r-- 1 root root 0 Apr 16 04:36 cpuinfo
-r--r--r-- 1 root root 0 Apr 16 04:36 crypto
-r--r--r-- 1 root root 0 Apr 16 04:36 devices
-r--r--r-- 1 root root 0 Apr 16 04:36 diskstats
dr-xr-xr-x 3 root root 0 Apr 16 04:36 driver
dr-xr-xr-x 3 root root 0 Apr 16 04:36 dynamic_debug
-r--r--r-- 1 root root 0 Apr 16 04:36 execdomains
-r--r--r-- 1 root root 0 Apr 16 04:36 fb
-r--r--r-- 1 root root 0 Apr 16 04:35 filesystems
dr-xr-xr-x 5 root root 0 Apr 16 04:35 fs
crw-rw-rw- 1 root root 1, 3 Apr 16 04:35 interrupts
-r--r--r-- 1 root root 0 Apr 16 04:36 iomem
-r--r--r-- 1 root root 0 Apr 16 04:36 ioports
dr-xr-xr-x 42 root root 0 Apr 16 04:35 irq
-r--r--r-- 1 root root 0 Apr 16 04:36 kallsyms
crw-rw-rw- 1 root root 1, 3 Apr 16 04:35 kcore
-r--r--r-- 1 root root 0 Apr 16 04:36 key-users
crw-rw-rw- 1 root root 1, 3 Apr 16 04:35 keys
-r-------- 1 root root 0 Apr 16 04:36 kmsg
-r-------- 1 root root 0 Apr 16 04:36 kpagecgroup
-r-------- 1 root root 0 Apr 16 04:36 kpagecount
-r-------- 1 root root 0 Apr 16 04:36 kpageflags
-r--r--r-- 1 root root 0 Apr 16 04:36 loadavg
-r--r--r-- 1 root root 0 Apr 16 04:36 locks
-r--r--r-- 1 root root 0 Apr 16 04:36 meminfo
-r--r--r-- 1 root root 0 Apr 16 04:36 misc
-r--r--r-- 1 root root 0 Apr 16 04:36 modules
lrwxrwxrwx 1 root root 11 Apr 16 04:36 mounts -> self/mounts
-rw-r--r-- 1 root root 0 Apr 16 04:36 mtrr
lrwxrwxrwx 1 root root 8 Apr 16 04:36 net -> self/net
-r-------- 1 root root 0 Apr 16 04:36 pagetypeinfo
-r--r--r-- 1 root root 0 Apr 16 04:36 partitions
dr-xr-xr-x 5 root root 0 Apr 16 04:36 pressure
-r--r--r-- 1 root root 0 Apr 16 04:36 schedstat
lrwxrwxrwx 1 root root 0 Apr 16 04:35 self -> 14
-r-------- 1 root root 0 Apr 16 04:36 slabinfo
-r--r--r-- 1 root root 0 Apr 16 04:36 softirqs
-r--r--r-- 1 root root 0 Apr 16 04:36 stat
-r--r--r-- 1 root root 0 Apr 16 04:36 swaps
dr-xr-xr-x 1 root root 0 Apr 16 04:35 sys
--w------- 1 root root 0 Apr 16 04:35 sysrq-trigger
dr-xr-xr-x 5 root root 0 Apr 16 04:36 sysvipc
lrwxrwxrwx 1 root root 0 Apr 16 04:35 thread-self -> 14/task/14
crw-rw-rw- 1 root root 1, 3 Apr 16 04:35 timer_list
dr-xr-xr-x 6 root root 0 Apr 16 04:36 tty
-r--r--r-- 1 root root 0 Apr 16 04:36 uptime
-r--r--r-- 1 root root 0 Apr 16 04:36 version
-r-------- 1 root root 0 Apr 16 04:36 vmallocinfo
-r--r--r-- 1 root root 0 Apr 16 04:36 vmstat
-r--r--r-- 1 root root 0 Apr 16 04:36 zoneinfoまず、sysは内部にkernel/,vm/,net/,fs/とかのディレクトリを持っており、カーネルの挙動を表す値がファイルとして置かれている。
cpuinfoやmeminfo、uptimeみたいな英語名のファイルの多くはシステム全体の状態を格納したものになっている。cpuinfoを例に取るとこんな感じ↓
processor : 0
vendor_id : AuthenticAMD
cpu family : 23
model : 49
model name : AMD EPYC 7B12
stepping : 0
microcode : 0xffffffff
cpu MHz : 2249.998
cache size : 512 KB
physical id : 0
siblings : 8
core id : 0
cpu cores : 4
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc
rep_good nopl nonstop_tsc cpuid extd_apicid tsc_known_freq pni pclmulqdq ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm cmp_lega
cy cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw topoext ssbd ibrs ibpb stibp vmmcall fsgsbase tsc_adjust bmi1 avx2 smep bmi2 rdseed adx smap clflushopt clwb sha_ni xsave
opt xsavec xgetbv1 clzero xsaveerptr arat npt nrip_save umip rdpid
bugs : sysret_ss_attrs null_seg spectre_v1 spectre_v2 spec_store_bypass retbleed smt_rsb srso ibpb_no_ret
bogomips : 4499.99
TLB size : 3072 4K pages
clflush size : 64
cache_alignment : 64
address sizes : 48 bits physical, 48 bits virtual
power management:
processor : 1
vendor_id : AuthenticAMD
cpu family : 23
...
そして今回一番重要なのは数字のみのディレクトリだ。
数字はプロセス番号を示していて、各プロセスの状態や設定情報が中に配置されている。
自分自身のプロセスには/proc/selfでアクセスすることができる。先程のls -la /procにおいてはself -> 14となっているため、python内でスポーンされたシェル自体のプロセス番号は14であると分かる。
プロセスの環境変数は/proc/[プロセス番号]/environに格納されている。
一応確認してみる。
PWD=/app
FLAG=🦙
SHLVL=1
_=/usr/bin/catシェルでenvと打った時と同じものが出力された。あとは、いま自分がターミナルからアクセスしているプロセスをスポーンさせた大もとのPythonが実行されているプロセスのenvironを覗く方法が分かれば良い!
今回はプロセス数が数えるほどしか無いから総当たりでもいけるかもしれないが、もう少し利口に解いてみる。
別プロセスによってスポーンされたプロセスは親プロセスについての情報を得るために$PPID(Parent Process ID)という環境変数を持っている1。名前の通り親プロセスのプロセス番号が入っている。
これを用いてserver.pyのプロセス情報に一発でアクセスする。
FLAG=Alpaca{XXXX}EZ!
最終的な解法リンクをコピーしました
strings /proc/$PPID/environ | grep FLAG
脚注
ちなみにPPIDそのものの情報は
/proc/self/statusに入っている ←