CSRF (1)

khm_studylog·2023년 2월 9일
0

web hacking

목록 보기
3/3

여러 기능과 입력받은 URL을 확인하는 봇이 구현된 서비스입니다.
CSRF 취약점을 이용해 플래그를 획득하세요.

CSRF

사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위(수정, 삭제, 등록 등)를 특정 웹 사이트에 요청하게 하는 공격 유형


vuln 페이지

[vuln(csrf) 페이지]에서는 vuln 파라미터로 alert(1)를 실행하는 스크립트 구문을 전달한다.

응답 화면에서는 script 구문이 필터링된 것을 볼 수 있다.

<script>alert(1)</script><*>alert(1) 로 필터링 되었다.
@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

[vuln 페이지]는 vuln 파라미터로 전달된 값에서 frame, script, on 을 *로 치환하는 필터링이 존재한다.

vuln 페이지는 스크립트 구문(script, iframe, on) 필터링이 있는 페이지이다.


memo 페이지

[memo 페이지]에 접속하면 memo 파라미터로 hello를 전달하고, 응답 화면에서는 hello를 출력한다.

여러번 접속해서 hello가 연속적으로 출력된다.

@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)

[memo 페이지]는 memo 파라미터로 전달된 값에서 < 를 &lt 로 치환하는 필터링이 존재한다.

memo 페이지는 최종적으로 flag가 출력되는 페이지다.


notice_flag 페이지

[notice flag 페이지]에 접속하면 Acess Denied 문구가 출력된다.

@app.route("/admin/notice_flag")
def admin_notice_flag():
    global memo_text
    if request.remote_addr != "127.0.0.1":
        return "Access Denied"
    if request.args.get("userid", "") != "admin":
        return "Access Denied 2"
    memo_text += f"[Notice] flag is {FLAG}\n"
    return "Ok"

[notice flag 페이지]는 접속 요청 사용자의 ip 주소가 127.0.0.1이 아니면 Access Denied 문자열을 출력하고

userid 파라미터 값이 admin이 아니면 Your not admin 문자열을 출력한다.

-> ip 주소가 127.0.0.1이고, userid가 admin이면 memo에 flag를 작성한다.

notice flag 페이지는 userid 값이 admin 이면 웹 서버 local 환경에서 flag를 memo로 추가하는 페이지다.


flag 페이지

[flag]페이지에 접속하면 제출하는 버튼이 보인다. 아까 csrf 페이지와 경로가 같은 것으로 보인다.

csrf 페이지에서 보았던 alert(1)를 출력하는 구문을 보내보자.

good 이라는 메세지가 출력되는게 끝이다.

@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", "")
        if not check_csrf(param):
            return '<script>alert("wrong??");history.go(-1);</script>'

        return '<script>alert("good");history.go(-1);</script>'

[flag 페이지]에서는 param 파라미터 값을 전달하고

check_csrf(param) 함수가 정상 동작한다면 good 문자열을 출력하고 check_csrf(param) 함수가 정상 동작하지 않으면, wrong 문자열을 출력한다.

flag 페이지는 csrf 페이지를 웹 서버 local IP로 접속하는 페이지이다.


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

read_url(url, cookie={"name": "name", "value": "value"})

read_url() 함수는 url과 cookie를 매개변수로 갖는 함수이다.

함수가 호출되면 cookie 변수 값이 "domain": "127.0.0.1"로 업데이트 된다.
그리고 driver 객체가 선언된다.

driver: 파이썬의 selenium 라이브러리를 이용한 크롬 웹 브라우저 제어(webdriver) 객체가 저장된 변수

driver는 http://127.0.0.1:8000/ 페이지에 접속하고, cookie에 저장된 값을 cookie로 추가한다.


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)

그리고 driver는 http://127.0.0.1:8000/vuln?param={urllib.parse.quote(param)} 에 접속한다.

urllib.parse.quote(url): 아스키 형식이 아닌 문자열을 url 인코딩 하는 함수이다.


우리는 flag 페이지를 이용해 웹 서버가 local에서 notice_flag 페이지에 접속하도록 한 뒤, memo로 flag를 출력하도록 하는 전략을 짤 수 있겠다.

flag 페이지에 csrf 파라미터 값을 입력하고 제출 버튼을 누르면 서버에서는 아래 url에 접속한다.

http://127.0.0.1:8000/csrf?csrf={사용자가 입력한 csrf 파라미터 값}

여기에서 csrf 파라미터 값만 조작할 수 있기 때문에 이 값으로 notice flag 페이지를 접속하도록 유도해야한다.

script, iframe, on 문자열이 치환되어 있지만, 단순 웹 페이지에 접속하는 구문만 실행시키려면

img 태그의 src 속성을 이용할 수 있다. 코드를 작성해보자.

<img src=/admin/notice_flag?userid=admin>

위 img 태그는 /admin/notice_flag?userid=admin 경로에서 이미지를 불러오는 태그인데

실제 이미지는 아니지만 일단 이미지를 불러오려는 시도를 하기 때문에 페이지에 접속하게 된다.

이 때 공격 구문이 성공할 수 있다. 진행시켜보자.


참고
https://mokpo.tistory.com/98

위 티스토리 토대로 따라해보았다..어렵다....

0개의 댓글