[Dreamhack/λλ¦Όν΅] pathtraversal
https://dreamhack.io/wargame/challenges/12
pathtraversal
μ¬μ©μμ μ 보λ₯Ό μ‘°ννλ API μλ²μ λλ€. Path Traversal μ·¨μ½μ μ μ΄μ©ν΄ /api/flagμ μλ νλκ·Έλ₯Ό νλνμΈμ! Reference Server-side Basic
dreamhack.io
[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λ‘ λ°κΏμ€ μ μλ€λ©΄ 보μμ΄ λ무 νμ ν κ² μλκ°...νλ μκ°μ΄ λ€μλλ°,
νμ μ΄ λ°©λ²μ μ¬μ©ν μ μλ κ²μΈμ§ κΆκΈνλ€.