0139 | DDoS : DNS Amplification Attack

บทความนี้เป็นส่วนหนึ่งของเนื้อหาเกี่ยวกับ DDoS (Distributed Denial of Service) หรือการพยายามโจมตีเพื่อให้เครื่องแม่ข่ายล่มครับ
ซึ่งจะทยอยเขียนตามอารมณ์และโอกาส และถ้าโดนยิงบ่อยๆ ก็จะเขียนบ่อยขึ้น (เอ๊ะยังไง?)

สำหรับ DNS Amplification Attack (หรืออาจเรียกว่า DNS Reflection Attack) นี้เป็นวิธีการโจมตีแบบใช้ตัวตายตัวแทนครับ
จริงๆ แล้วเรื่องนี้ค่อนข้างเก่าพอสมควร เพราะเป็นวิธีการโจมตีเครือข่ายที่ทำง่าย และป้องกันได้ยากมากวิธีนึงครับ โดยมีแผนผังการโจมตีดังนี้


(ภาพประกอบจาก CloudFlare blog)

สาเหตุที่เรียกว่า DNS Amplification หรือ DNS Reflection Attack นั้นก็มาจากลักษณะของการโจมตีครับ ด้วยตัวพื้นฐานของระบบ DNS นั้นจะใช้ protocol UDP ซึ่งเป็นการรับส่งข้อมูลแบบ stateless คือสามารถส่งข้อมูลออกไปได้ตั้งแต่ packet แรกที่ส่ง ดังนั้นจึงเกิดการปลอมแปลงได้ง่ายมากครับ โดยผู้โจมตีจะทำการปลอมแปลงเป็น IP ของเหยื่อ แล้วระดมส่งคำร้องขอทำการ resolve DNS ไปยัง DNS Server ที่ต่างๆ

และเมื่อ DNS Server ได้รับคำร้องแล้ว ก็จะตอบกลับไปหา IP ของเหยื่อ (ตามที่ผู้โจมตีปลอมมา) ทำให้เหยื่อได้รับ packet DNS ปริมาณมหาศาลตามลักษณะของรูปด้านบนครับ และที่บอกว่าเป็น Amplification Attack เพราะรูปแบบของคำร้อง DNS เหล่านี้ส่วนมากจะเป็น packet สั้นๆ แต่ขนาดของ packet ที่ DNS server จะต้องตอบกลับไปมีขนาดใหญ่มาก ทำให้ bandwidth ของเครื่องที่ถูกโจมตีเต็มได้ครับ

สำหรับวิธีป้องกันอาจแบ่งออกเป็นสองส่วนคือ ส่วนของ bandwidth และส่วนของการทำ stateful inspection ครับ คือหาเครื่อง firewall หรือ server ที่รองรับ bandwidth ปริมาณมากกว่าการโจมตีให้ได้ก่อน แล้วจึงทำการ drop ข้อมูลการถาม DNS แบบ stateful ครับ โดยตั้งให้ว่าถ้า firewall ไม่เจอคำร้องสำหรับ DNS request นั้น ให้ทำการ drop reply ที่ได้รับนั้นทิ้งไปประมาณนี้ครับ

iptables -A FORWARD -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
iptables -A FORWARD -j DROP

และสำหรับเจ้าของ server ถ้าเป็นไปได้ “อย่า” เปิด recursion resolve เพื่อไม่ให้เครื่อง server ของตัวเองกลายเป็นเครื่องที่ไปโจมตีชาวบ้านครับ

Tags: ,

0138 | function get url ผ่าน https ใช้แทน curl

วันก่อนเจอบั๊กระหว่าง curl + openssl + centos 6 นิดหน่อย
ที่ทำให้ curl และ app ทุกอย่างที่ใช้ libcurl ต่อไปหา server ที่เป็น https บางตัวบนบางเงื่อนไขไม่ได้
หาข้อมูลไปก็พบว่าต้องไปแก้ที่ฝั่ง server (จะบ้าเรอะ server ชาวบ้าน) ไม่ก็ต้องแก้ code ใหม่ให้ใช้ library อย่างอื่นแทน ก็เลยเกิดมาเป็น function นี้ครับ

มี requirement นิดหน่อยคือ PHP 5.3.4 ขึ้นไป และ เปิด allow_url_fopen ไว้ใน php.ini

feature
=> custom header ได้
=> รองรับ cookie โดยอัตโนมัติ
=> method ได้ทั้ง get และ post (รวมถึง post แบบใส่ data ด้วย)
=> url ได้ทั้ง http และ https (มี validate url เผื่อไว้ด้วย)
=> บังคับ verify ssl certificate ที่ฝั่งปลายทางด้วยว่า cert ถูกต้อง (file get content ปกติจะไม่ verify)
=> ใช้ ca file ของ OS (แก้ได้)

<?php
function httpquery($url, $method = 'GET', $headers = array(), $body = '', $returnheader = false, $followlocation = true){
    global $cookies;
    $reqinfo = parse_url($url);
    if (empty($reqinfo['host']) || empty($reqinfo['path'])) return false;
    if (empty($reqinfo['scheme'])) $reqinfo['scheme'] = 'http';
    if ($reqinfo['scheme'] != 'http' && $reqinfo['scheme'] != 'https') {
        trigger_error("Unsupported scheme '".$reqinfo['scheme']."'");
        return false;
    }
    if (empty($reqinfo['port'])) {
        if ($reqinfo['scheme'] == 'https')
            $reqinfo['port'] = 443;
        else
            $reqinfo['port'] = 80;
    }
 
    $headers['User-Agent'] = (!isset($headers['User-Agent']) ? 'Mozilla/4.0 (compatible; icez.net http client)' : $headers['User-Agent']);
    $headers['Host'] = $reqinfo['host'];
    $headers['Accept'] = '*/*';
 
 
    $cdata = array();
    foreach($cookies as $ckey => $cval) {
        $cdata[] = $ckey.'='.$cval;
    }
    $headers['Cookie'] = implode('; ', $cdata); // http_build_query($cookies);
    unset($cdata);
 
    if ($method == 'POST') {
        $headers['Content-Type'] = (isset($headers['Content-Type']) ? $headers['Content-Type'] : 'application/x-www-form-urlencoded');
    }
 
    $headerstring = '';
    foreach ($headers as $header => $content) {
        $headerstring .= $header.': '.$content."\r\n";
    }
 
    $reqpath = $reqinfo['path'].(isset($reqinfo['query']) ? '?'.$reqinfo['query'] : '');
 
    $opts = array('ssl' => array(
            'verify_peer' => true,
            'cafile' => '/etc/pki/tls/certs/ca-bundle.crt'
        ),
        'http' => array(
            'method' => $method,
            'follow_location' => ($followlocation ? 1 : 0),
            'ignore_errors' => true,
            'timeout' => 15,
            'header' => $headerstring
        )
    );
    if (!empty($body)) $opts['http']['content'] = $body;
    $context = stream_context_create($opts);
 
    $stream = fopen($url, 'r', false, $context);
    $meta = stream_get_meta_data($stream);
    $content = stream_get_contents($stream);
    if (!is_array($meta['wrapper_data'])) {
        trigger_error("Invalid HTTP Header Response");
    }
    $header = '';
 
    foreach($meta['wrapper_data'] as $header) {
        if (strpos($header, ':') !== false) {
            $line = explode(':', $header, 2);
            if (strtolower($line[0]) == 'set-cookie') {
                $cookie = explode(';', trim($line[1]), 2);
                if (strpos($cookie[0], '=') !== false) {
                    //valid cookie
                    $cdata = explode('=', $cookie[0], 2);
                    $cookies[$cdata[0]] = $cdata[1];
                }
            }
        }
    }
    fclose($stream);
    if ($returnheader) $content = implode("\r\n", $meta['wrapper_data'])."\r\n\r\n".$content;
    return $content;
}
?>