CSRF
CSRF는 임의 이용자의 권한으로 임의 주소에 HTTP요청을 보낼 수 있는 취약점입니다. 공격자는 임의 이용자의 권한으로 서비스 기능을 사용해 이득을 취할 수 있습니다.
CSRF공격에 성공하기 위해서는 공격자가 작성한 악성 스크립트를 이용자가 실행해야 합니다. 이는 공격자가 이용자에게 메일을 보내거나, 게시판에 글을 작성해 이용자가 이를 조회하도록 유도하는 방법이 있습니다. 여기서 말하는 악성 스크립트는 HTTP요청을 보내는 코드로, 아래에서 요청을 보내는 스크립트를 작성하는 방법에 대해 알아보도록 하겠습니다.
CSRF공격 스크립트는 HTML또는 JavaScript를 통해 작성할 수 있습니다. 위 Figure5는 HTML으로 작성된 스크립트입니다. 이미지를 불러오는 img태그를 사용하거나 웹 페이지에 입력된 양식을 전송하는 form태그를 사용하는 방법이 있습니다. 이 두개의 태그를 사용해 HTTP요청을 보내면 HTTP헤더인 Cookie에 이용자의 인증 정보가 포함됩니다.
HTML img태그 공격 예시
<img src='http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337' width=0px height=0px>
Javascript 공격 코드 예시
/* 새 창 띄우기 */
window.open('http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337');
/* 현재 창 주소 옮기기 */
location.href = 'http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337';
location.replace('http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337');
CSRF 실습
위와 같은 코드에서 HTML태그를 통해, 계좌 잔고를 1,000,000이상으로 늘리려면 어떤 코드를 작성해야 할까요
<img src="/sendmoney?to=dreamhack&amount=1000000">
을 url에 삽입하기만 하면 됩니다.
XSS CSRF차이
XSS와 CSRF모두 스크립트를 웹 페이지에 작성해 공격한다는 점에서 매우 유사합니다. 그렇다면 두 취약점의 공통점과 차이점은 무엇일까요?
공통점
두 개의 취약점은 모두 클라이언트를 대상으로 하는 공격이며, 이용자가 악성 스크립트가 포함된 페이지에 접속하도록 유도해야 합니다.
차이점
두 개의 취약점은 공격에 있어 서로 다른 목적을 가집니다. XSS는 인증 정보인 세션및 쿠키 탈취를 목적으로 하는 공격이며, 공격할 사이트의 오리진에서 스크립트를 실행시킵니다.
CSRF는 이용자가 임의 페이지에 HTTP요청을 보내는 것을 목적으로 하는 공격입니다. 또한 공격자는 악성 스크립트가 포함된 페이지에 접근한 이용자의 권한으로 웹 서비스의 임의 기능을 실행할 수 있습니다.
CSRF 실습
#!/usr/bin/python3
from flask import Flask, request, render_template, make_response, redirect, url_for
from selenium import webdriver
import urllib
import os
app = Flask(__name__)
app.secret_key = os.urandom(32)
try:
FLAG = open("./flag.txt", "r").read()
except:
FLAG = "[**FLAG**]"
users = {
'guest': 'guest',
'admin': FLAG
}
session_storage = {}
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"})
try:
options = webdriver.ChromeOptions()
for _ in [
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage",
]:
options.add_argument(_)
driver = webdriver.Chrome("/chromedriver", options=options)
driver.implicitly_wait(3)
driver.set_page_load_timeout(3)
driver.get("http://127.0.0.1:8000/")
driver.add_cookie(cookie)
driver.get(url)
except Exception as e:
driver.quit()
print(str(e))
# return str(e)
return False
driver.quit()
return True
def check_csrf(param, cookie={"name": "name", "value": "value"}):
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
return read_url(url, cookie)
@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"}')
@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
@app.route("/flag", methods=["GET", "POST"])
def flag():
if request.method == "GET":
return render_template("flag.html")
elif request.method == "POST":
param = request.form.get("param", "")
session_id = os.urandom(16).hex()
session_storage[session_id] = 'admin'
if not check_csrf(param, {"name":"sessionid", "value": session_id}):
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
@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("not found user");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
resp.set_cookie('sessionid', session_id)
return resp
return '<script>alert("wrong password");history.go(-1);</script>'
@app.route("/change_password")
def change_password():
pw = request.args.get("pw", "")
session_id = request.cookies.get('sessionid', None)
try:
username = session_storage[session_id]
except KeyError:
return render_template('index.html', text='please login')
users[username] = pw
return 'Done'
app.run(host="0.0.0.0", port=8000)
서버의 코드는 다음과 같았습니다. 문제를 푼 방식을 알려드리겠습니다.
- 먼저 /flag로 post요청을 날리게 되면 일단 session_storage[session_id] = "admin"을 해 주는 것을 확인해 볼 수 있습니다.
- 그리고 저희는 guest로 로그인을 하게 되면 cookie로 session_id를 집어 넣는 것을 볼 수 있습니다.
- 이와 함꼐 쿠키에 session_id를 집어 넣습니다.
- 즉 저희는 다른 사용자의 입장에서 /change_password를 호출해 주면 될 것 같다는 생각을 하면 됩니다.
- flag에다 /change_password?pw=1234라고 임의로 쳐주게 되면 users[username] 여기서 username은 admin인데 ( session_id로 가져온 ) 즉 admin의 비빈을 1234로 설정한 것이게 됩니다.
즉 다른 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위 ( 다른 곳으로 HTTP요청을 보냄 ) 을 수행해서 비번을 바꿔서 flag를 확인할 수 있게 된 겁니다.
여기서는 XSS가 막혀있었습니다. 그 이유는 frame script on이 막혀 있었기 때문입니다.
'School > Security' 카테고리의 다른 글
[ Web Hacking ] - SQL Injection (0) | 2022.05.29 |
---|---|
[ Reverse Engineering ] - ABOUT (0) | 2022.05.28 |
[ Web Hacking ] - XSS Bypass (0) | 2022.05.28 |
[ Web Hacking ] - XSS (0) | 2022.05.27 |
[ Web Hacking ] - Mitigation: Same Origin Policy (0) | 2022.05.27 |