CTF에서 웹이라는 분야는 웹 서비스의 특성상 다시 풀어보기가 어려운 분야이다.
헌데 우연히 GoSec 2014에 출제되었던 문제를 풀어볼 수 있는 기회가 생기게되어 이에 대한 라이트업을 작성해보고자 한다.

다시 풀어볼 수 있는 링크는 이와같다. http://challenges.ringzer0team.com:10158/
(만약 Write-Up을 제공함으로써 문제에 대한 가치를 홰손하게 되는 경우가 발생하면 바로 글을 삭제하도록 하겠다, 또한 Sql injection에 사용된 코드는 올리지 않았으며 문제를 풀기위한 핵심 쿼리를 올려놓았다. 만약 공부를 위해 코드가 필요한 경우는 2peopledesu@gmail.com으로 메일을 바란다.)

문제를 풀어볼 수 있는 링크가 있기에 스크린샷은 제외하고 설명토록 하겠다.

http://challenges.ringzer0team.com:10158/?u=test&p=test
위와 같이 ID / PW에 test / test를 넣게 되면
Invalid username cannot find hot mom. 이라는 문구가 나오게 된다.

http://challenges.ringzer0team.com:10158/?u=admin&p=test
위와 같이 ID / PW에 admin / test를 넣게 되면
Invalid password cannot find hot mom. 이라는 문구가 나오게 된다.

즉 우리가 여기서 얻을 수 있는 단서는 ID는 admin을 사용하고 있음을 알게되었다.

여기서 쿼리문의 구조를 생각하며 게싱을 통하여
admin') and ('1'='1 이러한 구문을 삽입시키면 Sql Injection이 통한다는 사실을 알게되었다.
admin') and ('1'='1를 넣을 시 Invalid password cannot find hot mom.(참)이 나오게 되며
admin') and ('1'='0를 넣을 시 Invalid username cannot find hot mom.(거짓)이 나오게 된다.

여기서 얻을 수 있는 두 번째 단서는 Blind sql injection이 가능함을 알게 되었다.

Blind sql injection을 한 과정을 조금 자세히 불친절하게 올려보자면
')OR((SELECT(LENGTH(password))FROM(users)WHERE(username)=('admin'))=172)#
우선 이러한 쿼리를 통해서 password의 길이를 유추해내었다.(password 라는 컬럼을 사용하는 것은 몇 차례의 게싱을 통하여 알아내었다.)

여기서 얻을 수 있는 두 번째 단서는 Password는 뭔가로 암호화가 되어있다는 사실이다.
(일반적으로 172글자나 되는 비밀번호, 암호화 방식은 존재하지 않는다.)
아마 Private key, Public key를 이용하여 암호화가 되어있다고 생각할 수 있다.
이로써 이 문제는 단순히 DB에서 값을 꺼내는 Sql injection이 아님을 유추할 수 있다.

admin') and (substr(password,1,1) = (CHAR(97))) and ('1'='1

이러한 쿼리를 이용하여 비밀번호를 유추해낼 수 있는데 단순한 Blind Injection을 이용하면
DB에서 Password를 빼오는데 많은 시간이 소요되니 Binary Search와 같은 방식을 사용하도록 하자.

최종적으로 뽑아낸 Password는 아래와 같다.

aWzode4/**********/nq5yOPZg+A7Jh+OdMlKuCwJW7F+hByRisBsjTzeB67WGT
yifOJwUEAo16FarJkYMDJh4TbG5wvIvg3ZWx89gbbVCnF7jN71KgPVtV+t5rcN9iLbz6Q
Dy3UVsjnjq0uk57mMC2ANjNMl5QkCatfE=

역시 무언가로 암호화 된 Password이다.
그리고 이 Password를 복호화하기 위해선 어딘가 숨어있는 Key 혹은 하드코딩된 무언가를 찾아야 할 것이다.

여기서 백업파일이 남아있을지 찾기 위해서 index.phps php.bak php~ 등 많은 수를 시도해보았으나 아쉽게도 아니였다.

고민을 하던 도중 Mysql 함수 중 하나인 load_file을 생각해내었다.
https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_load-file
(load_file Reference Manual)

먼저 여기서 '절대경로'를 찾아내야하는데 문제 출제자가 정말 미치지 않는 이상 당연히 Default경로 일거라고 생각을 하여 어떤 서버를 사용했는지 먼저 알아내는 과정이 필요하였다.

http://challenges.ringzer0team.com:10158/2pp.php

이와 같은 경로를 들어가면 Apache/2.4.18을 사용하고 있음을 알게된다.
Apache/2.4.18의 Default 경로는

<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
AllowOverride None
Order allow,deny
allow from all

/var/www/이다 .

우선 /var/www/index.php를 열어봐야 한다고 생각하여

admin') and (substr(load_file('/var/www/index.php'),1,1) = (CHAR(97))) and ('1'='1
이러한 쿼리로 index.php의 내용을 얻게 되었고

index.php의 코드 중

$key = GetKey('/askldjl****lawe/key.private');
openssl_private_decrypt(base64_decode($row['password']), $output, openssl_get_privatekey($key));

이와 같은 내용이 있었다.

아까 사용했던 load_file 쿼리를 이용해서 /askldjllawe/key.private를 읽어내면
-----BEGIN RSA PRIVATE KEY-----
~
-----END RSA PRIVATE KEY-----
라는 값이 나오게 된다.

아까 얻었던 index.php의 코드를 이용하여 복호화를 하면
FLAG-**************************를 얻게된다.