챌린지/Wargame.kr

bughela - DB is really GOOD

하루히즘 2020. 12. 22. 18:48

wargame.kr의 9번째 문제인 DB is really GOOD이다.

문제 힌트에서는 이 데이터베이스의 종류와 유저 이름과 데이터베이스 간 관계를 파악하라고 되어있다. Start 버튼을 눌러 문제를 확인해보자.

간단한 입력폼과 로그인 창이 뜬다. 따로 소스를 보여주는 링크도 없고 HTML을 확인해봐도 자바스크립트로 입력값 admin을 제한할 뿐 별다른 기능은 없다. 그렇다면 admin을 입력해보면 어떻게 될까?

일단 첫번째로 admin으로 접근하지 말라는 간단한 alert() 메시지가 뜬다. 소스 코드를 확인해보면 이 입력폼은 "/memo.php"라는 곳으로 데이터를 보내고 있기 때문에 웹 프록시로 중간에 값을 admin으로 수정하거나 Postman을 활용하여 admin 데이터를 전송해보자.

그래도 역시 admin으로 접속하지 말라는 메시지가 뜬다. 아예 admin을 직접적으로 입력할 수 없도록 막아놓은 모양인데 그렇다면 어떻게 우회할 수 있을까? 일단 필터링 상태를 알아보기 위해 기본적인 따옴표를 포함해서 이것저것 입력해보자.

"admin'"처럼 따옴표를 하나 붙여서 입력했더니 그대로 로그인이 되는걸 볼 수 있다. 메모를 남기는 간단한 텍스트 박스와 버튼, 그리고 다른 사람들이 남긴 메모들을 볼 수 있는데 여기서 눈여겨 볼 것은 php 엔진에서 발생한 경고 메시지다.

"Notice: A non well formed numeric value encountered in /var/www/html/db_is_really_good/read.php on line 8"를 해석해보면 read.php의 8번째 줄에서 에러가 발생했다고 하는데 현재 웹사이트의 스크립트를 보면 해당 계정으로 로그인했을 때 저장된 메모 값을 불러와서 현재 문서에 써주는 역할인 것 같다.

jQuery를 활용하고 있다.

현재로서는 세션으로 로그인한 계정을 파악하고 있는 것인지 read.php에 직접 접속해봐도 아무런 값을 얻을 수 없었다. Postman으로 접속해보면 오히려 메인 페이지로 돌려보내는 것을 볼 수 있었는데 그렇다면 이 정보를 어떻게 활용할 수 있을까? 일단 추측해 볼 수 있는 점은 현재 PHP 엔진에서 이런 에러 메시지를 그대로 출력해주고 있기 때문에 무언가 다른 정보를 더 얻을 수 있지 않을까 하는 것이다. 이런 문제는 OWASP Top 10에서도 6번째인 Security Misconfiguration에 속한다고 할 수 있다. 에러 처리 과정에서 웹사이트의 디렉토리를 노출하고 있기 때문에 도구를 이용한 퍼징으로 인해 중요 정보가 누출될 수 있는 것이다.

OWASP Top 10 - 2017 Korean Translation

그렇다면 어떤 값을 넣어보면서 테스트해야 할까? 따옴표부터 괄호, 물음표, 꺽쇠 등 오만가지 특수문자를 다 넣어보면서 테스트해보았다. 그 결과 "admin/"라는 계정으로 접속했을 때 다음과 같은 에러 메시지를 볼 수 있었다.

에러 메시지를 읽어보니 "sqlite3.php" 파일에서 처리 과정 중에 발생한 오류로 데이터베이스 파일을 열 수 없다고 한다. 두번째 줄에 오류가 발생한 구문을 노출하고 있는데 "SQLite3 -> open('./db/wkrm_admin...')"에서 에러가 발생했다는 것으로 보아 SQLite3라는 객체의 open() 메소드를 호출하는 과정에서 문제가 발생했다는 것을 알 수 있다. 이런 open() 함수는 대개 어떤 파일을 읽거나 쓰기 위해 사용될텐데 마침 위에서 에러 메시지로 데이터베이스 파일을 열 수 없다고 하고 있으니 아마 이 매개변수로 전달된 uri는 데이터베이스 파일의 위치일 것이다. 그런데 왜 에러가 발생하는 것일까? 아마 사용자가 입력한 계정명을 기반으로 데이터베이스 파일을 참조하기 때문일 것이다.

...
SQLite3 -> open("./db/wkrm_guest.db") // it opens 'wkrm_guest.db' which is located in './db'
SQLite3 -> open("./db/wkrm_admin/.db") // it opens '.db' which is located in './db/wkrm_admin/'
...

이게 어떻게 문제가 될 수 있는지는 위의 코드와 같다. 사용자 입력을 이스케이프하지 않고 그대로 파일명에 반영하기 때문에 우리가 입력한 "admin/" 문자열의 "/"는 디렉토리를 의미하게 되어 존재하지 않는 디렉토리에서 파일을 찾게 되는 것이다. 확실하게 예를 보이기 위해 다시 메인 페이지로 돌아가서 "1234/" 같은 계정으로 로그인하면 동일한 에러가 발생하는 것을 볼 수 있다.

여러번 "/"가 들어간 계정명을 입력하면서 발생하는 오류 패턴을 보면 사용자의 입력값 앞에 "wkrm_"라는 문자열을 붙이는 것 같다. 뒤에는 어떤 확장자가 들어간다는 정보는 나와있지 않지만 아마 ".db"같은 확장자가 붙으리라 예상된다. 이를 확인하기 위해 오류 메시지의 정보를 기반으로 추측한 데이터베이스가 저장되는 곳(/var/www/html/db_is_really_good/db/, 즉 /db)에 접속해보자.

여기서도 디렉토리의 Index of를 누출하는 Directory Listing 문제가 있다. 이 역시 위처럼 Security Misconfiguration 문제에 해당하며 지금까지 사용자들이 입력한 계정명에 따라 생성된 수많은 데이터베이스 파일을 보고 다운받을 수 있다. 문제에서 설명한 사용자 이름과 데이터베이스와의 관계가 바로 이것을 말하는 것이었는데, 그렇다면 여기서 admin 계정의 데이터베이스 파일을 찾을 수 있지 않을까?

검색 결과 wkrm_admin.db 라는 파일을 찾을 수 있었다. 다른 파일들과 다르게 2014년에 생성된 파일로 아마 이것이 해답이 될 것이다. 이를 클릭하거나 주소창에 입력해서 직접 다운로드 받을 수 있다. 이렇게 다운받은 데이터베이스 파일은 별다른 암호화나 그런게 없기 때문에 직접 열어보거나 SQLite DB Browser를 이용하여 데이터를 확인할 수 있다.

이렇게 얻은 플래그 문자열의 주소로 이동해보면 다음과 같이 플래그 값을 얻을 수 있다.

 

 

이번 문제는 사실 진작에 이 db 디렉토리를 찾았지만 admin 계정의 db가 존재하지 않는 줄 알고 다른데에서 삽질하다가 간신히 풀게 되었다. admin 계정의 접속을 막았기 때문에 admin 계정의 db는 존재하지 않는다는 잘못된 가정에 사로잡혀서 시간을 좀 낭비하게 된 경험이었다.