https://dreamhack.io/wargame/challenges/12
[1] ๋ฌธ์ ๋ถ์
๋ฌธ์ ์ค๋ช ์ ์ํ๋ฉด Path Traversal ์ทจ์ฝ์ ์ ์ด์ฉํด์ผ ํ๋ค.
๋ฐ๋ผ์ ๋จผ์ Path Traversal ์ทจ์ฝ์ ์ ๋ํด ์์๋ณด๊ณ ์ ํ๋ค.
Path Traversal ์ทจ์ฝ์ ์ด๋?
์ผ๋ฐ์ ์ผ๋ก ๊ฒฝ๋ก ์ํ(Path Traversal)๋ผ๊ณ ๋ ๋ถ๋ฆฌ๋ฉฐ, ์ด ์ทจ์ฝ์ ์ ์ด์ฉํ์ฌ ๊ณต๊ฒฉ์๋ ์์ฉ ํ๋ก๊ทธ๋จ์ด๋ ์๋ฒ์ ํ์ผ ์์คํ ๊ฒฝ๋ก๋ฅผ ๋ฒ์ด๋๋ ค๊ณ ์๋ํ๋ค. ๋ณดํต์ ํ์ผ ๊ฒฝ๋ก์ "../" ๋๋ ์์ ๋๋ ํ ๋ฆฌ ๊ตฌ๋ถ์๋ฅผ ์ฝ์ ํ์ฌ ํ์ผ์ ๋ํ ์๋์ ์ธ ๊ฒฝ๋ก๋ฅผ ๋ณ๊ฒฝํ์ฌ ๊ณต๊ฒฉ์ ์๋ํ๋ค. ์ด๋ฅผ ํตํด ๊ณต๊ฒฉ์๋ ์ผ๋ฐ์ ์ผ๋ก๋ ์ก์ธ์คํ ์ ์๋ ํ์ผ์ด๋ ๋๋ ํ ๋ฆฌ์ ์ก์ธ์คํ ์ ์๋ค.
์๋ฅผ ๋ค์ด, ์น ์์ฉ ํ๋ก๊ทธ๋จ์์ ํ์ผ์ ๋ก๋ํ ๋ ์ฌ์ฉ์ ์ ๋ ฅ์ ๊ฒฝ๋ก์ ์ง์ ์ฝ์ ํ๋ ๊ฒฝ์ฐ, ๊ณต๊ฒฉ์๊ฐ ํด๋น ์ ๋ ฅ์ ์กฐ์ํ์ฌ ์์คํ ํ์ผ์ ์ก์ธ์คํ ์ ์๋ค. ์ด๋ฅผ ๋ฐฉ์งํ๊ธฐ ์ํด์๋ ์ ๋ ฅ์ ๊ฒ์ฆํ๊ณ , ์ ๋ขฐํ ์ ์๋ ํ์ผ ๊ฒฝ๋ก๋ง ํ์ฉํ๊ฑฐ๋, ํ์ผ ์์คํ ์ก์ธ์ค์ ๋ํ ์ ์ ํ ๊ถํ์ ์ค์ ํ๋ ๋ฑ์ ๋ณด์ ์กฐ์น๋ฅผ ์ทจํ ์ ์๋ค.
์ฐธ๊ณ : https://jini23-lamp.tistory.com/entry/PathDirectory-Traversal
https://owasp.org/www-community/attacks/Path_Traversal
# ์น ํ์ด์ง ๋ถ์
๋ฌธ์ ๋งํฌ์ ์ ์ํ๋ฉด ์ฒ์ ๋์ค๋ ํ์ด์ง์ด๋ค.
Get User Info ๋ฅผ ๋๋ฌ ๋ณด์๋ค.
์๋ง ์ ๊ธฐ์ ์ ๋ ฅ๊ฐ์ ๋ฃ์ ๋
"../" --> ์ด๋ฐ ํ์์ ๋ฃ์ด์ฃผ์ด์ Path Traversal ์ทจ์ฝ์ ์ ์ ์ฉํ ์ ์๊ฒ ๋์ง ์์๊น ์ถ๋ค.
์ฐ์ ์ ๋ ฅ์นธ์ "guest" ๊ฐ ๊ธฐ๋ณธ๊ฐ์ผ๋ก ์ ๋ ฅ์ด ๋์ด์๊ธธ๋ ๊ทธ๋๋ก View ๋ฅผ ๋๋ฌ๋ณด์๋ค.
์ด๋ฐ ๊ฒฐ๊ณผ ํ๋ฉด์ด ๋์จ๋ค.
๋ฌด์จ ์๋ฏธ์ผ๊น?...
"admin"๋ ์ ๋ ฅํด๋ณด์๋ค.
์ด๋ฒ์ ๋ค๋ฅธ ๊ฒฐ๊ณผ๋ฌผ์ด ๋์๋ค.
"userid"์ "admin"์ ๊ฐ์ผ๋ก๋ userid ์ ๋ ฅ ์นธ์ ๋ค์ด๊ฐ ๊ฐ์ด ๋ฌด์กฐ๊ฑด ๋์ค๋ ๊ฑธ๊น?
"level"์ ์ด๋ป๊ฒ ์ ํด์ง๋ ๊ฑธ๊น?
Path Traversal ์ทจ์ฝ์ ์ด "../" ํ์์ ์ ๋ ฅํ์ฌ ๋ค๋ฅธ ๋๋ ํ ๋ฆฌ๋ฅผ ๊ฒฝ๋ก๋ฅผ ์ํํ ์ ์๋ ๋ฐฉ์์ผ๋ก ์ด๋ฃจ์ด์ง๋ค๊ณ ํ์ผ๋๊น
"../"๋ฅผ ์ ๋ ฅํด๋ณด์๋ค.
{} ์์ ์๋ฌด๋ฐ ๊ฐ๋ ๋์ค์ง ์๋๋ค.
์๋ง ํํฐ๋ง ๋๋ ๋ฌด์ธ๊ฐ๊ฐ ์ค์ ๋์ด์์ง ์์๊น... ํ๋ ์๊ฐ์ด ๋ค์๋ค.
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/static/css/bootstrap.min.css">
<link rel="stylesheet" href="/static/css/bootstrap-theme.min.css">
<link rel="stylesheet" href="/static/css/non-responsive.css">
<title>Index Path Traversal</title>
<style type="text/css">
.important { color: #336699; }
</style>
</head>
<body>
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">Path Traversal</a>
</div>
<div id="navbar">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#contact">Contact</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav>
<div class="container">
<a href="/get_info">Get User Info</a>
</div> <!-- /container -->
<!-- Bootstrap core JavaScript -->
<script src="/static/js/jquery.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
</body>
</html>
์น ํ์ด์ง ์์ค์ฝ๋์์๋ ๋ฑํ ์์๋ผ ์ ์๋ ๊ฒ ์์ด์ ๋ฌธ์ ํ์ผ์ ๋ค์ด๋ฐ์๋ณด๊ธฐ๋ก ํ๋ค.
# ๋ฌธ์ ํ์ผ ๋ถ์
app.py ํ๋๋ฅผ ๋ค์ด๋ฐ์ ์ ์๋ค.
#!/usr/bin/python3
from flask import Flask, request, render_template, abort
from functools import wraps
import requests
import os, json
users = {
'0': {
'userid': 'guest',
'level': 1,
'password': 'guest'
},
'1': {
'userid': 'admin',
'level': 9999,
'password': 'admin'
}
}
def internal_api(func):
@wraps(func)
def decorated_view(*args, **kwargs):
if request.remote_addr == '127.0.0.1':
return func(*args, **kwargs)
else:
abort(401)
return decorated_view
app = Flask(__name__)
app.secret_key = os.urandom(32)
API_HOST = 'http://127.0.0.1:8000'
try:
FLAG = open('./flag.txt', 'r').read() # Flag is here!!
except:
FLAG = '[**FLAG**]'
@app.route('/')
def index():
return render_template('index.html')
@app.route('/get_info', methods=['GET', 'POST'])
def get_info():
if request.method == 'GET':
return render_template('get_info.html')
elif request.method == 'POST':
userid = request.form.get('userid', '')
info = requests.get(f'{API_HOST}/api/user/{userid}').text
return render_template('get_info.html', info=info)
@app.route('/api')
@internal_api
def api():
return '/user/<uid>, /flag'
@app.route('/api/user/<uid>')
@internal_api
def get_flag(uid):
try:
info = users[uid]
except:
info = {}
return json.dumps(info)
@app.route('/api/flag')
@internal_api
def flag():
return FLAG
application = app # app.run(host='0.0.0.0', port=8000)
# Dockerfile
# ENTRYPOINT ["uwsgi", "--socket", "0.0.0.0:8000", "--protocol=http", "--threads", "4", "--wsgi-file", "app.py"]
์ ์ฒด ์ค์ ์ ๋ ฅ ๊ฐ ์ ์ฅ ๋ถ๋ถ์ ์ฝ๋๋ฅผ ๋ถ์ํด๋ณด์.
elif request.method == 'POST':
userid = request.form.get('userid', '')
info = requests.get(f'{API_HOST}/api/user/{userid}').text
return render_template('get_info.html', info=info)
์ ๋ ฅ๋ userid ๊ฐ์ ์ ๊ทธ๋ฆผ์์ ๋ณผ ์ ์๋ฏ์ด, /api/user/{userid} ์ ์ ์ฅ๋๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
@app.route('/api/flag')
@internal_api
def flag():
return FLAG
FLAG๋ /api/flag์ ์ ์ฅ์ด ๋๋ค.
์ ์ฅ๋ ๊ฐ์ /api/user/{userid} ์,
FLAG ๊ฐ์ /api/flag ์ ์ ์ฅ์ด ๋๊ธฐ ๋๋ฌธ์,
../flag ๋ฅผ ์
๋ ฅํ๋ฉด /api/flag ์ ์ ๊ทผํ ์ ์์ ๊ฒ์ด๋ผ๊ณ ์๊ฐํ๊ณ ../flag๋ฅผ ์
๋ ฅํด๋ณด์๋ค.
์ ๋ฉ๋๋ค...
์ฌ๊ธฐ์๋ถํฐ ๊ตฌ๊ธ๋ง์ ํตํด ๋ฌธ์ ํ์ด๋ฅผ ์งํํ์๋ค.
users = {
'0': {
'userid': 'guest',
'level': 1,
'password': 'guest'
},
'1': {
'userid': 'admin',
'level': 9999,
'password': 'admin'
}
}
์ด ๋ถ๋ถ์ ๋ณด๋ฉด guest ์ admin์ key-value ์์ ๋ณผ ์ ์๋ค.
console์ฐฝ์ ์ด์ฉํด์ ์ด ๊ฐ์ ../flag๋ก ๋ฐ๊ฟ์ฃผ์๋ค.
guest๋ฅผ ์ ๋ ฅํ๋ฉด flag๊ฐ ๋จ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.
[2] ๋๋ ์
์น ํ์ด์ง์ ๋ณ์๊ฐ์ console๋ก ๋ฐ๊ฟ์ค ์ ์๋ค๋ฉด ๋ณด์์ด ๋๋ฌด ํ์ ํ ๊ฒ ์๋๊ฐ...ํ๋ ์๊ฐ์ด ๋ค์๋๋ฐ,
ํญ์ ์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ์ ์๋ ๊ฒ์ธ์ง ๊ถ๊ธํ๋ค.
'SWLUG > CTF ๋ฌธ์ ํ์ด (2)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Dreamhack/๋๋ฆผํต] web-misconf-1 (0) | 2024.05.22 |
---|---|
[Dreamhack/๋๋ฆผํต] simple-web-request (0) | 2024.05.15 |
[Dreamhack/๋๋ฆผํต] baby-union (0) | 2024.05.08 |
[Dreamhack/๋๋ฆผํต] PHPreg (0) | 2024.05.07 |
[Dreamhack/๋๋ฆผํต] php7cmp4re (0) | 2024.04.28 |