@app.route('/admin/notice_flag') # notice_flag 페이지 라우팅
def admin_notice_flag():
global memo_text # 메모를 전역변수로 참조
if request.remote_addr != '127.0.0.1': # 이용자의 IP가 로컬호스트가 아닌 경우
return 'Access Denied' # 접근 제한
if request.args.get('userid', '') != 'admin': # userid 파라미터가 admin이 아닌 경우
return 'Access Denied 2' # 접근 제한
memo_text += f'[Notice] flag is {FLAG}\n' # 위의 조건을 만족한 경우 메모에 FLAG 기록
return 'Ok' # Ok 반환
@app.route("/vuln") # vuln 페이지 라우팅 (이용자가 /vuln 페이지에 접근시 아래 코드 실행)
def vuln():
param = request.args.get("param", "").lower() # 이용자가 입력한 param 파라미터를 소문자로 변경
xss_filter = ["frame", "script", "on"] # 세 가지 필터링 키워드
for _ in xss_filter:
param = param.replace(_, "*") # 이용자가 입력한 값 중에 필터링 키워드가 있는 경우, '*'로 치환
return param # 이용자의 입력 값을 화면 상에 표시
1. vuln 함수
2. memo 함수
/memo 페이지를 구성하는 코드.
이용자가 전달한 memo 파라미터 값 기록, render_template 함수를 통해 출력함.
@app.route('/memo') # memo 페이지 라우팅
def memo(): # memo 함수 선언
global memo_text # 메모를 전역변수로 참조
text = request.args.get('memo', '') # 이용자가 전송한 memo 입력값을 가져옴
memo_text += text + '\n' # 메모의 마지막에 새 줄 삽입 후 메모에 기록
return render_template('memo.html', memo=memo_text) # 사이트에 기록된 메모를 화면에 출력
3. /admon/notice_flag 페이지 구성 코드
@app.route('/admin/notice_flag') # notice_flag 페이지 라우팅
def admin_notice_flag():
global memo_text # 메모를 전역변수로 참조
if request.remote_addr != '127.0.0.1': # 이용자의 IP가 로컬호스트가 아닌 경우
return 'Access Denied' # 접근 제한
if request.args.get('userid', '') != 'admin': # userid 파라미터가 admin이 아닌 경우
return 'Access Denied 2' # 접근 제한
memo_text += f'[Notice] flag is {FLAG}\n' # 위의 조건을 만족한 경우 메모에 FLAG 기록
return 'Ok' # Ok 반환
로컬 호스트 127.0.0.1에서 접근
userid 파라미터가 admin일 경우 메모에 FLAG 작성, 조건 만족 안하며 접근 제한 메시지 출력함.
/admin/notice_flag 페이지 자체는 모두가 접근할 수 있고, userid 파라미터에 admin 값을 넣는 것도 가능함.
하지만 일반 유저가 해당 페이지에 접근할 때의 IP 주소는 조작할 수 없음. 따라서 일반 유저의 IP가 자신의 컴퓨터를 의미하는 로컬호스트 IP가 되는 것은 불가능하기 때문에, 이 페이지에 단순히 접근하는 것만으로는 플래그를 획득할 수 없음.
로컬호스트(Localhost)
컴퓨터 네트워크에서 사용하는 호스트명으로 자기 자신의 컴퓨터를 의미함.
IPv4 : 127.0.0.1
IPv6 : 00:00:00:00:00:00:00:01
4. /flag 페이지 구성 코드
GET : 이용자에게 URL을 입력받는 페이지 제공.
POST : param 파라미터 값을 가져와 check_csrf 함수의 인자로 넣고 호출함.
check_csrf 함수는 인자를 다시 CSRF 취약점이 발생하는 URL의 파라미터로 설정. read_url 함수를 이용해 방문함.
이때 방문하는 URL은 서버가 동작하고 있는 로컬호스트의 이용자가 방문하는 시나리오이기 때문에 127.0.0.1의 호스트로 접속하게 됨.
read_url 함수는 셀레늄을 이용해 URL 방문함.
@app.route("/flag", methods=["GET", "POST"]) # flag 페이지 라우팅 (GET, POST 요청을 모두 받음)
def flag():
if request.method == "GET": # 이용자의 요청이 GET 메소드인 경우
return render_template("flag.html") # 이용자에게 링크를 입력받는 화면을 출력
elif request.method == "POST": # 이용자의 요청이 POST 메소드인 경우
param = request.form.get("param", "") # param 파라미터를 가져온 후,
if not check_csrf(param): # 관리자에게 접속 요청 (check_csrf 함수)
return '<script>alert("wrong??");history.go(-1);</script>'
return '<script>alert("good");history.go(-1);</script>'
def check_csrf(param, cookie={"name": "name", "value": "value"}):
url = f"http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)}" # 로컬 URL 설정
return read_url(url, cookie) # URL 방문
def read_url(url, cookie={"name": "name", "value": "value"}):
cookie.update({"domain": "127.0.0.1"}) # 관리자 쿠키가 적용되는 범위를 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) # 크롬 로딩타임을 위한 타임아웃 3초 설정
driver.set_page_load_timeout(3) # 페이지가 오픈되는 타임아웃 시간 3초 설정
driver.get("http://127.0.0.1:8000/") # 관리자가 CSRF-1 문제 사이트 접속
driver.add_cookie(cookie) # 관리자 쿠키 적용
driver.get(url) # 인자로 전달된 url에 접속
except Exception as e:
driver.quit() # 셀레늄 종료
print(str(e))
# return str(e)
return False # 접속 중 오류가 발생하면 비정상 종료 처리
driver.quit() # 셀레늄 종료
return True # 정상 종료 처리
취약점 분석
/vuln 기능 : 이용자의 입력 값 페이지 출력.
frame, script, on 세 가지 키워드 필터링 -> xss 공격 불가능.
하지만 필터링 키워드 이외의 꺽쇠 (<,>) 를 포함한 다른 키워드와 태그는 사용할 수 있음 -> CSRF 공격을 수행할 수 있음
익스플로잇
/vuln 페이지에서 CSRF 공격 가능.
공격 코드가 삽입된 페이지로 다른 이용자가 방문할 경우, 의도하지 않은 페이지로 요청을 전송하는 시나리오의 익스플로잇을 구상해야함.
/admin/notice_flag 페이지를 로컬호스트에서 접근해야함. 플래그를 얻기 위해서.
CSRF 공격을 이용해서 /vuln 페이지 방문하는 이용자가 /admin/notice_flag 페이지로 요청을 전송하도록 하는 공격 코드 작성.
로컬 호스트 환경의 이용자가 임의 페이지를 방문하게 하려면 /flag 페이지를 이용해야함.
테스트베드 생성
CSRF 취약점 발생 여부를 확인하기 위해 HTTP 응답을 받을 웹 서버가 필요함.
CSRF 취약점 테스트
테스트베드를 생성했다면 CSRF 공격 코드를 작성하고, 취약점 발생 여부를 확인함.
<img> 태그 사용.
취약점이 발생하는 페이지에 해당코드 삽입.
<img src="https://jugwwka.request.dreamhack.games">
gbmwgxd로 입력해야하는데 잘못해서 다시함
생성한 테스트베드에 요청이 옴.
로컬호스트에 위치하는 이용자가 /admin/notice_flag 페이지를 방문하도록 해야하기 때문에 아래와 같이 공격코드를 작성해야함.
<img src="/admin/notice_flag?userid=admin" />
userid 파라미터가 admin인지 검사하는 부분이 존재하므로 해당 문자열을 포함해야함.
DH{11a230801ad0b80d52b996cbe203e83d}
'Dreamhack' 카테고리의 다른 글
SQL Injection(1) (0) | 2022.08.11 |
---|---|
SQL (0) | 2022.08.11 |
암호학 Stage 4 (0) | 2022.08.05 |
암호학 Stage 3 (0) | 2022.08.04 |
암호학 Stage 2 (0) | 2022.07.30 |