CSRF (Cross-Site Request Forgery)
- 사이트 간 요청 위조의 줄임말로 인증된 사용자가 웹 애플리케이션에 특정 요청을 보내도록 유도하는 공격 행위이다
- 이 문제에서는 비밀번호를 바꾸는데 필요한 세션 아이디를 공격자가 만들어서 권한을 얻어 비밀번호를 변경하여 플래그를 취득하는 방식으로 진행된다
@app.route("/")
def index():
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
except KeyError:
return render_template('index.html', text='please login')
return render_template('index.html', text=f'Hello {username}, {"flag is " + FLAG if username == "admin" else "you are not an admin"}')
` username `이 'admin'이면 flag를 출력하는 것 같다.
@app.route("/vuln")
def vuln():
param = request.args.get("param", "").lower()
xss_filter = ["frame", "script", "on"]
for _ in xss_filter:
param = param.replace(_, "*")
return param
XSS에 대한 필터링에 대한 코드인 것 같다.
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
elif request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
try:
pw = users[username]
except:
return '<script>alert("user not found");history.go(-1);</script>'
if pw == password:
resp = make_response(redirect(url_for('index')) )
session_id = os.urandom(8).hex()
session_storage[session_id] = username
token_storage[session_id] = md5((username + request.remote_addr).encode()).hexdigest()
resp.set_cookie('sessionid', session_id)
return resp
return '<script>alert("wrong password");history.go(-1);</script>'
로그인에 관련된 코드이다
로그인 성공 시 ...
if pw == password:
resp = make_response(redirect(url_for('index')) )
session_id = os.urandom(8).hex()
session_storage[session_id] = username
token_storage[session_id] = md5((username + request.remote_addr).encode()).hexdigest()
resp.set_cookie('sessionid', session_id)
` session_id `와 ` ` token `를 발급해주는 것 같다.
여기서 token은 'username' + 'remote_addr'의 값을 md5로 암호화 한 값을 발급해준다.
@app.route("/change_password")
def change_password():
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
csrf_token = token_storage[session_id]
except KeyError:
return render_template('index.html', text='please login')
pw = request.args.get("pw", None)
if pw == None:
return render_template('change_password.html', csrf_token=csrf_token)
else:
if csrf_token != request.args.get("csrftoken", ""):
return '<script>alert("wrong csrf token");history.go(-1);</script>'
users[username] = pw
return '<script>alert("Done");history.go(-1);</script>'
비밀번호 바꾸는 페이지에 관련된 코드이다.
위에서 발급 받은 토큰은 ` csrf_token `인 것으로 보인다. 여기서 csrf_token이 일치하면 비밀번호를 바꿀 수 있는 것으로 보인다.
즉 이 문제는 계정의 'csrf_token' 값만 알 수 있다면, 그 계정의 비밀번호를 우리가 임의로 바꿀 수도 있는 것이다.
이 부분이 취약점으로 작용할 것 같다.
그렇다면 위에서 token을 발급해주는 방식으로 우리가 admin 계정의 토큰도 얻을 수 있을 것 같으니 한 번 얻어보자.
token_storage[session_id] = md5((username + request.remote_addr).encode()).hexdigest()
이 부분이 토큰 발급 기능을 구현하는 코드이다
여기서 보면 username + remote.addr 값을 합친 어떤 문자열을 'md5' 방식으로 암호화 한 값이 토큰으로 발급되는 것 같다.
그렇다면 여기서 remote.addr이 무엇인가 한 번 알아보자.
구글에 검색해보니 대충 서버 ip주소를 저 곳에 입력하면 될 것 같다.
코딩하기 귀찮아서 인터넷에 있는 md5 암호화 도구를 찾아서 admin127.0.0.1을 암호화 해주니 결과가 나왔다.
저 결과값이 admin 계정의 csrf_token 값일 것이다.
즉 저 값을 가지고 admin 계정의 비밀번호를 바꾸어 로그인하면 플래그를 얻을 수 있다.
하지만.. 어떻게 admin 계정의 비밀번호를 바꿀 수 있을까
여기서 vuln(csrf) page와 flag 페이지를 이용할 수 있다
vuln(scrf) page에 있는 img태그를 이용해서 flag페이지에 있는 입력창에 pw를 변경할 값과 csrf_token값을 넣어주면 성공적으로 비밀번호가 변경 될 것이다.
<img src="/change_password?pw=admin&csrftoken=7505B9C72AB4AA94B1A4ED7B207B67FB">
이렇게 입력해주었다.
그러면 admin 계정의 비밀번호는 admin으로 변경될 것이다.
admin 계정에 바뀐 비밀번호로 로그인을 하니 플래그가 성공적으로 출력되었다.
'DreamHack > Web hacking' 카테고리의 다른 글
[LEVEL-1] NoSQL-CouchDB (1) | 2023.12.26 |
---|---|
[LEVEL-1] Command Injection Advanced (1) | 2023.12.25 |
[LEVEL-1] simple-ssti (1) | 2023.12.06 |
[LEVEL-1] Apache htaccess (1) | 2023.12.06 |
[LEVEL-Beginner] pathtraversal (1) | 2023.12.02 |