基本情報リンクをコピーしました

問題文リンクをコピーしました

シークレットが環境変数にあるなら上書きしちゃえば安全だぜ!

おっ、そうだな。

作業ログリンクをコピーしました

ファイルをダウンロードして展開。

$tree flag-over/
flag-over/ ├── Dockerfile ├── compose.yaml └── server.py 1 directory, 3 files

とてもシンプルなプログラム。

Dockerfile
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"]
compose.yaml
services: sandbox: build: . restart: unless-stopped ports: - ${PORT:-1337}:1337 environment: - FLAG=Alpaca{REDACTED}

docker関連のファイルは特に問題なさそう。
FLAGという環境変数を仕込んであるからこれを奪取するのね。

本命のPythonコードを見てみよう。

server.py
import os import subprocess assert os.getenv("FLAG").startswith("Alpaca{") subprocess.run(["bash", "-i"], env={"FLAG": "🦙"})

これまたシンプル。
まず環境変数FLAGがちゃんと登録されているかを確認してから、
subprocessでFLAGの中身を🦙に上書きしてスポーンさせ、ユーザーからの操作を受け付けてる。

実際にアクセスして確認してみよう。

$nc [サーバーのIP] [ポート番号]
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はカーネルが今持っている情報をファイルっぽく見せる仮想ファイルシステムで、システム全体の情報、各プロセスごとの情報、実行中に変えられるカーネル設定が並んでいる。

$ls -la /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/とかのディレクトリを持っており、カーネルの挙動を表す値がファイルとして置かれている。

cpuinfomeminfouptimeみたいな英語名のファイルの多くはシステム全体の状態を格納したものになっている。cpuinfoを例に取るとこんな感じ↓

$cat /proc/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に格納されている。

一応確認してみる。

$strings /proc/self/environ
PWD=/app FLAG=🦙 SHLVL=1 _=/usr/bin/cat

シェルでenvと打った時と同じものが出力された。あとは、いま自分がターミナルからアクセスしているプロセスをスポーンさせた大もとのPythonが実行されているプロセスのenvironを覗く方法が分かれば良い!

今回はプロセス数が数えるほどしか無いから総当たりでもいけるかもしれないが、もう少し利口に解いてみる。

別プロセスによってスポーンされたプロセスは親プロセスについての情報を得るために$PPID(Parent Process ID)という環境変数を持っている1。名前の通り親プロセスのプロセス番号が入っている。

これを用いてserver.pyのプロセス情報に一発でアクセスする。

$strings /proc/$PPID/environ | grep FLAG
FLAG=Alpaca{XXXX}

EZ!

最終的な解法リンクをコピーしました

strings /proc/$PPID/environ | grep FLAG

脚注

  1. ちなみにPPIDそのものの情報は/proc/self/statusに入っている