dfir it!

responding to incidents with candied bacon

Black Hat Arsenal Peepdf Challenge - Walkthrough

B-Sides London Challenge was supposed to be a one time thing. However when the peepdf’s author creates a challenge it’s hard to say no to that! Don’t try to be like me and learn things the hard way. Trust me on this one and take my advice beforehand. Before you even consider reading this walkthrough update your tool! Otherwise you will spend a long time trying to solve it.

If you want to follow along the link to the challenge can be found below:

Analysis

Let’s see what’s inside the PDF file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# peepdf -i peepdf_challenge_blackhat.pdf 
File: peepdf_challenge_blackhat.pdf
MD5: 0f3f32ed91ffac18827af99740c6e256
SHA1: 9052d13d049e078b4d8e30260036f39436c575c0
SHA256: 3b8436512a13ab0145f919b0c7855f409bee5146ef931c4ef86819fe0f05acbc
Size: 75276 bytes
Version: 1.5
Binary: True
Linearized: False
Encrypted: False
Updates: 0
Objects: 14
Streams: 5
Comments: 0
Errors: 0
  
Version 0:
  Catalog: 1
  Info: 6
  Objects (14): [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
  Compressed objects (4): [8, 9, 10, 7]
  Streams (5): [4, 5, 11, 13, 12]
      Xref streams (1): [12]
      Object streams (1): [11]
      Encoded (5): [4, 5, 11, 13, 12]
  Suspicious elements:
      /Names: [1, 13]
      /AA: [13]
      /JS: [13]
      /JavaScript: [13]
      /EmbeddedFiles: [1]
      /EmbeddedFile: [13]

Output of peepdf provides information about various suspicious elements which include EmbeddedFiles. Interestingly enough it also contains JS and AA elements which are often starting point of any analysis. Reviewing EmbeddedFiles reveals additional metadata about the file:

1
2
3
4
5
6
7
8
PPDF> object 1

<< /MarkInfo << /Marked true >>
/StructTreeRoot 7 0 R
/Pages 2 0 R
/Type /Catalog
/Lang es-ES
/Names << /EmbeddedFiles << /Names [ peepdf.pdf 14 0 R ] >> >> >>

/Names suggests that we have a pdf inside of another pdf. Dumping the file can be achieved by:

1
Stream 13 > peepdf.pdf

Analysing peepdf.pdf:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# peepdf -i peepdf.pdf 
File: peepdf.pdf
MD5: 3895e6a72976929f8f5b414ffad6d42f
SHA1: 70f20031a4019bdd97b25290fc3ebc9cb4b7ddc9
SHA256: e9958b248574775e32c8bd6d2a38f1689a1ec9124f1c62e5f887ec3fe93830b5
Size: 13379 bytes
Version: 1.7
Binary: True
Linearized: False
Encrypted: True (RC4 128 bits)
Updates: 0
Objects: 22
Streams: 5
Comments: 0
Errors: 1

Version 0:
  Catalog: 1
  Info: 12
  Objects (22): [1, 2, 3, 4, 5, 6, 7, 8, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24]
      Errors (2): [5, 16]
  Streams (5): [4, 6, 8, 22, 24]
      Encoded (2): [6, 8]
  Objects with JS code (4): [5, 16, 19, 24]
  Suspicious elements:
      /Names: [1, 14]
      /AA: [3]
      /JS: [3, 13, 15, 17, 18, 23]
      /JavaScript: [3, 7, 13, 15, 17, 18, 23]
      getAnnots (CVE-2009-1492): [16]

We see a very handy feature of peepdf which is information about known CVEs. Instead of jumping to the first JS object, let’s review the relationship between the objects to better understand structure of the file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
PPDF> tree

/Catalog (1)
  /Pages (2)
      /Page (3)
          stream (4)
          /Pages (2)
          hexstring (5)
          /Annot (20)
          /Annot (21)
              stream (22)
  /JavaScript (7)
      /Names (14)
          /Action /JavaScript (13)
              stream (6)
          /Action /JavaScript (15)
              stream (8)
          /Action /JavaScript (17)
              hexstring (16)
          /Action /JavaScript (18)
              hexstring (19)
          /Action /JavaScript (23)
              stream (24)
string (11)
/Info (12)
  string (11)

It seems that object 5 uses annotations and as we know this pdf likely contains a known getAnnots vulnerability it’s definitely worth looking at:

1
2
3
4
5
6
7
8
9
PPDF> object 5

var version = app.viewerVersion.toString().split(".")[0];
if (version > 10){
  app.alert({cTitle:"Peepdf Challenge",cMsg:"You should try with an older version of Adobe Reader ;)"});
  this.closeDoc(true);
}
else{
  peepdf(r(a,x.d(this.info.author)));

The above code checks the version of the software (the reason being is getAnnots vulnerability). If version is greater than 10 it closes the document. If not the following function peepdf(r(a,x.d(this.info.author))); is executed.

Let’s check the object containing getAnnots vulnerability:

1
2
3
4
5
6
7
8
9
10
11
PPDF> object 16

app.doc.syncAnnotScan();
var z="";
var an = app.doc.getAnnots({nPage:0});
var s = an[this.numPages].subject;
var buf = s.split(/x1/);
for (var n = 0; n < buf.length; n++) {
  z += String.fromCharCode("0" + "x" + buf[n]);
}
peepdf(z);

This function takes the annotation’s subject, splits it and performs decoding.

If you take a closer look at tree output once again one of the /Annot objects contains additional stream:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
PPDF> object 21

<< /Rect [ 100 180 300 210 ]
/Type /Annot
/Subtype /Text
/Subj 22 0 R
/Name /Comment >>

PPDF> object 22

<< /Length 2914 >>
stream
76x161x172x120x178x120x13dx120x17bx10ax109x12fx12fx168x174x174x170x13ax12fx12fx177x177x177x12ex177x165x162x174x16fx16fx16cx16bx169x174x12ex169x16ex166x16fx12fx10ax109x16bx13ax120x122x152x153x154x155x156x157x158x159x15ax161x162x163x164x141x142x143x144x145x146x147x148x149x16ex16fx170x171x172x173x174x175x176x177x178x179x17ax130x131x132x133x134x14ax14bx14cx14dx14ex14fx150x151x165x166x167x168x169x16ax16bx16cx16dx135x136x137x138x139x12bx12fx13dx122x12cx10ax109x164x13ax120x166x175x16ex163x174x169x16fx16ex120x128x169x16ex170x175x174x129x120x17bx10ax109x109x176x161x172x120x16bx16bx120x13dx120x122x122x13bx10ax109x109x176x161x172x120x163x131x12cx120x163x132x12cx120x163x133x12cx120x163x134x13bx10ax109x109x176x161x172x120x165x131x12cx120x165x132x12cx120x165x133x12cx120x165x134x13bx10ax109x109x176x161x172x120x169x120x13dx120x130x13bx10ax109x109x169x16ex170x175x174x120x13dx120x169x16ex170x175x174x12ex172x165x170x16cx161x163x165x128x12fx15bx15ex141x12dx15ax161x12dx17ax130x12dx139x15cx12bx15cx12fx15cx13dx15dx12fx167x12cx120x122x122x129x13bx10ax109x109x177x168x169x16cx165x120x128x169x120x13cx120x169x16ex170x175x174x12ex16cx165x16ex167x174x168x129x120x17bx10ax109x109x109x165x131x120x13dx120x174x168x169x173x12ex16bx12ex169x16ex164x165x178x14fx166x128x169x16ex170x175x174x12ex163x168x161x172x141x174x128x169x12bx12bx129x129x13bx10ax109x109x109x165x132x120x13dx120x174x168x169x173x12ex16bx12ex169x16ex164x165x178x14fx166x128x169x16ex170x175x174x12ex163x168x161x172x141x174x128x169x12bx12bx129x129x13bx10ax109x109x109x165x133x120x13dx120x174x168x169x173x12ex16bx12ex169x16ex164x165x178x14fx166x128x169x16ex170x175x174x12ex163x168x161x172x141x174x128x169x12bx12bx129x129x13bx10ax109x109x109x165x134x120x13dx120x174x168x169x173x12ex16bx12ex169x16ex164x165x178x14fx166x128x169x16ex170x175x174x12ex163x168x161x172x141x174x128x169x12bx12bx129x129x13bx10ax109x109x109x163x131x120x13dx120x128x165x131x120x13cx13cx120x132x129x120x17cx120x128x165x132x120x13ex13ex120x134x129x13bx10ax109x109x109x163x132x120x13dx120x128x128x165x132x120x126x120x131x135x129x120x13cx13cx120x134x129x120x17cx120x128x165x133x120x13ex13ex120x132x129x13bx10ax109x109x109x163x133x120x13dx120x128x128x165x133x120x126x120x133x129x120x13cx13cx120x136x129x120x17cx120x165x134x13bx10ax109x109x109x16bx16bx120x13dx120x16bx16bx120x12bx120x153x174x172x169x16ex167x12ex166x172x16fx16dx143x168x161x172x143x16fx164x165x128x163x131x129x13bx10ax109x109x109x169x166x120x128x165x133x120x121x13dx120x136x134x129x120x17bx16bx16bx120x13dx120x16bx16bx120x12bx120x153x174x172x169x16ex167x12ex166x172x16fx16dx143x168x161x172x143x16fx164x165x128x163x132x129x13bx17dx10ax109x109x109x169x166x120x128x165x134x120x121x13dx120x136x134x129x120x17bx16bx16bx120x13dx120x16bx16bx120x12bx120x153x174x172x169x16ex167x12ex166x172x16fx16dx143x168x161x172x143x16fx164x165x128x163x133x129x13bx17dx10ax109x109x17dx10ax109x109x172x165x174x175x172x16ex120x16bx16bx13bx10ax109x17dx10ax17dx13b
endstream

Using SpiderMonkey we can decode this subject to more readable form:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
remnux@remnux:~/Desktop/PeePdf$ more decode.js 
//app.doc.syncAnnotScan();

var shellcode='76x161x172x120x178x120x13dx120x17bx10ax109x12fx12fx168x174x174x170x13ax12fx12fx177x177x177x12ex177x165x162x174x16fx16fx16cx16bx169x174x12ex169x16ex166x16fx12fx10ax109
x16bx13ax120x122x152x153x154x155x156x157x158x159x15ax161x162x163x164x141x142x143x144x145x146x147x148x149x16ex16fx170x171x172x173x174x175x176x177x178x179x17ax130x131x132x133x134x14ax
14bx14cx14dx14ex14fx150x151x165x166x167x168x169x16ax16bx16cx16dx135x136x137x138x139x12bx12fx13dx122x12cx10ax109x164x13ax120x166x175x16ex163x174x169x16fx16ex120x128x169x16ex170x175x1
74x129x120x17bx10ax109x109x176x161x172x120x16bx16bx120x13dx120x122x122x13bx10ax109x109x176x161x172x120x163x131x12cx120x163x132x12cx120x163x133x12cx120x163x134x13bx10ax109x109x176x16
1x172x120x165x131x12cx120x165x132x12cx120x165x133x12cx120x165x134x13bx10ax109x109x176x161x172x120x169x120x13dx120x130x13bx10ax109x109x169x16ex170x175x174x120x13dx120x169x16ex170x175
x174x12ex172x165x170x16cx161x163x165x128x12fx15bx15ex141x12dx15ax161x12dx17ax130x12dx139x15cx12bx15cx12fx15cx13dx15dx12fx167x12cx120x122x122x129x13bx10ax109x109x177x168x169x16cx165x
120x128x169x120x13cx120x169x16ex170x175x174x12ex16cx165x16ex167x174x168x129x120x17bx10ax109x109x109x165x131x120x13dx120x174x168x169x173x12ex16bx12ex169x16ex164x165x178x14fx166x128x1
69x16ex170x175x174x12ex163x168x161x172x141x174x128x169x12bx12bx129x129x13bx10ax109x109x109x165x132x120x13dx120x174x168x169x173x12ex16bx12ex169x16ex164x165x178x14fx166x128x169x16ex17
0x175x174x12ex163x168x161x172x141x174x128x169x12bx12bx129x129x13bx10ax109x109x109x165x133x120x13dx120x174x168x169x173x12ex16bx12ex169x16ex164x165x178x14fx166x128x169x16ex170x175x174
x12ex163x168x161x172x141x174x128x169x12bx12bx129x129x13bx10ax109x109x109x165x134x120x13dx120x174x168x169x173x12ex16bx12ex169x16ex164x165x178x14fx166x128x169x16ex170x175x174x12ex163x
168x161x172x141x174x128x169x12bx12bx129x129x13bx10ax109x109x109x163x131x120x13dx120x128x165x131x120x13cx13cx120x132x129x120x17cx120x128x165x132x120x13ex13ex120x134x129x13bx10ax109x1
09x109x163x132x120x13dx120x128x128x165x132x120x126x120x131x135x129x120x13cx13cx120x134x129x120x17cx120x128x165x133x120x13ex13ex120x132x129x13bx10ax109x109x109x163x133x120x13dx120x12
8x128x165x133x120x126x120x133x129x120x13cx13cx120x136x129x120x17cx120x165x134x13bx10ax109x109x109x16bx16bx120x13dx120x16bx16bx120x12bx120x153x174x172x169x16ex167x12ex166x172x16fx16d
x143x168x161x172x143x16fx164x165x128x163x131x129x13bx10ax109x109x109x169x166x120x128x165x133x120x121x13dx120x136x134x129x120x17bx16bx16bx120x13dx120x16bx16bx120x12bx120x153x174x172x
169x16ex167x12ex166x172x16fx16dx143x168x161x172x143x16fx164x165x128x163x132x129x13bx17dx10ax109x109x109x169x166x120x128x165x134x120x121x13dx120x136x134x129x120x17bx16bx16bx120x13dx1
20x16bx16bx120x12bx120x153x174x172x169x16ex167x12ex166x172x16fx16dx143x168x161x172x143x16fx164x165x128x163x133x129x13bx17dx10ax109x109x17dx10ax109x109x172x165x174x175x172x16ex120x16
bx16bx13bx10ax109x17dx10ax17dx13b'

var z = "";
//var an = app.doc.getAnnots({
//    nPage: 0
//});
//var s = an[this.numPages].subject;
var buf = shellcode.split(/x1/);
for (var n = 0; n < buf.length; n++) {
    z += String.fromCharCode("0" + "x" + buf[n]);
}
//peepdf(z);
print(z)

After executing the code following output appears:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var x = {
  //http://www.webtoolkit.info/
  k: "RSTUVWXYZabcdABCDEFGHInopqrstuvwxyz01234JKLMNOPQefghijklm56789+/=",
  d: function (input) {
      var kk = "";
      var c1, c2, c3, c4;
      var e1, e2, e3, e4;
      var i = 0;
      input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
      while (i < input.length) {
          e1 = this.k.indexOf(input.charAt(i++));
          e2 = this.k.indexOf(input.charAt(i++));
          e3 = this.k.indexOf(input.charAt(i++));
          e4 = this.k.indexOf(input.charAt(i++));
          c1 = (e1 << 2) | (e2 >> 4);
          c2 = ((e2 & 15) << 4) | (e3 >> 2);
          c3 = ((e3 & 3) << 6) | e4;
          kk = kk + String.fromCharCode(c1);
          if (e3 != 64) {kk = kk + String.fromCharCode(c2);}
          if (e4 != 64) {kk = kk + String.fromCharCode(c3);}
      }
      return kk;
  }
};

Keep in mind that after opening the original file the following code peepdf(r(a,x.d(this.info.author))); will be executed. We now have found the definition of associative array x which contains function d and takes this.info.author as a parameter.

this.info.author can be found by reviewing the Info object:

1
2
3
4
5
6
7
PPDF> object 12

<< /CreationDate D:19820925000000
/Author 11 0 R
/Producer Peepdf Library X
/ModDate D:20150805153000
/Creator Scribus 1.3.3.14 >>

and following the /Author reference:

1
2
3
PPDF> object 11

aeJrtFmIbya6v42tZEOXZgxaCyxiAUeIbxx5aTxaBymjbndwWRHtAU9rBy8/qhEtAxZctFmIbyamrl2vSDZtCFyRsTt/Zz2qAiNMBFenZyZiZUewcVaGBTOrqyDjZheusWqZdzencI87Ag1GADDcwgJwB0pibGqaZ1dGCfisbEaxugDHT2NjwhmCcSi/aTinaDdSZ3dGZSVjrF2CCx8udzxZqjmyaz2AweJVAU8Bqxe5VhSaCD5Ftfiwbet+Zo2+BDJVCFxHbEamrhKeZxfFtfDIBjt9bTiuBS9atXi0ZDa6ZhfBAS1vAXissxt/Zz2qAiNtwUmFaeHPq4xur1abcXEScetLrGyEAS1tvGpqXymPbheYthNAAUivbWtqchyECDmXAzyppyDoAUmYtg1uaniUZDa6bGfpAHNtC3iabf1+qhxuZxpaCFWrBDHhdhfZZHNtC3ibbfZLZh8udS9ZAU1wCS17blEaCx8YtF1IB2t5bUDuaDEZAzxBsyxiZ4tuXfmsanitZDI6dhWptE8RZgxwsyH/ATiuZempC08BCIq6RUpuAxEZAzass1fhqFDHrxJpZndsZyZJdlWXd08SaFVwuWHbZ0fAADjZVzHsZyN/bG5ptitcYUmuuWHGZo2VCy5ZdU8wsypPdhfGADZXA3imZyp5cY2jdS9ZBhxaB2t5bUEptE8YtFRUsxtJZzKpCf8aaFDIZWt7bGiuASdpCFeGZWtgbTmubRDutGmIZxV/Zl2HaHIZWTeaafZJq4xwcVaGdUibpSpkZzOavERcSFDBZyaxqD2pASNdAki5aypkbhfGAx5bwFmlCESxqDjIdRHUZ3itZDI6AhItbRZXA3fss1jhqFDHrypACGmwAEpLAlutCDmranHScFdhdhIpri1mATxbbyW6SUWtCDtACgJwsWN5TzKrri18ZhErcfR7c0tttV1IvYpY

Alright, we still need to find definitions of peepdf(), r(), and a variable.

Let’s use the output from the tree command and go through all the /JavaScript elements one by one:

1
2
3
4
5
6
7
8
9
10
11
12
/JavaScript (7)
  /Names (14)
      /Action /JavaScript (13)
          stream (6)
      /Action /JavaScript (15)
          stream (8)
      /Action /JavaScript (17)
          hexstring (16)
      /Action /JavaScript (18)
          hexstring (19)
      /Action /JavaScript (23)
          stream (24)

First stream contains value of variable a:

1
2
3
4
5
6
7
8
9
10
11
12
13
PPDF> object 6

<< /Filter [ /ASCIIHexDecode /DCTDecode ]
/Length 408
/ColorSpace /DeviceGray
/Type /XObject
/BitsPerComponent 8
/Height 1
/Width 24
/Subtype /Image >>
stream
   var a="QkhQMzNwZGY=";
endstream

Second stream explains that peepdf() is really an eval() function:

1
2
3
4
5
6
7
8
9
10
11
<< /Filter [ /ASCIIHexDecode /DCTDecode ]
/Length 396
/ColorSpace /DeviceGray
/Type /XObject
/BitsPerComponent 8
/Height 1
/Width 24
/Subtype /Image >>
stream
peepdf=eval;//PADDINGGGG
endstream

Third /JavaScript element contains definition of the function r() which seems to be a decoding function that accepts key and message as parameters:

1
2
3
4
5
6
7
8
9
PPDF> js_beautify object 19

function r(key, data) {
    var kk = "";
    for (var i = 0; i < data.length; i++) {
        kk += String.fromCharCode(data.charCodeAt(i) ^ key.charCodeAt(i % key.length));
    }
    return kk
}

It looks like we finally have all the the pieces of the puzzle to execute the code in SpiderMonkey:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function d(input) {
  var k = "RSTUVWXYZabcdABCDEFGHInopqrstuvwxyz01234JKLMNOPQefghijklm56789+/="
  var kk = "";
  var c1, c2, c3, c4;
  var e1, e2, e3, e4;
  var i = 0;
  input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
  while (i < input.length) {
      e1 = k.indexOf(input.charAt(i++));
      e2 = k.indexOf(input.charAt(i++));
      e3 = k.indexOf(input.charAt(i++));
      e4 = k.indexOf(input.charAt(i++));
      c1 = (e1 << 2) | (e2 >> 4);
      c2 = ((e2 & 15) << 4) | (e3 >> 2);
      c3 = ((e3 & 3) << 6) | e4;
      kk = kk + String.fromCharCode(c1);
      if (e3 != 64) {kk = kk + String.fromCharCode(c2);}
      if (e4 != 64) {kk = kk + String.fromCharCode(c3);}
      }
      return kk;
}
var author="aeJrtFmIbya6v42tZEOXZgxaCyxiAUeIbxx5aTxaBymjbndwWRHtAU9rBy8/qhEtAxZctFmIbyamrl2vSDZtCFyRsTt/Zz2qAiNMBFenZyZiZUewcVaGBTOrqyDjZheusWqZdzencI87Ag1GADDcwgJwB0pibGqaZ1dGCfisbEaxugDHT2NjwhmCcSi/aTinaDdSZ3dGZSVjrF2CCx8udzxZqjmyaz2AweJVAU8Bqxe5VhSaCD5Ftfiwbet+Zo2+BDJVCFxHbEamrhKeZxfFtfDIBjt9bTiuBS9atXi0ZDa6ZhfBAS1vAXissxt/Zz2qAiNtwUmFaeHPq4xur1abcXEScetLrGyEAS1tvGpqXymPbheYthNAAUivbWtqchyECDmXAzyppyDoAUmYtg1uaniUZDa6bGfpAHNtC3iabf1+qhxuZxpaCFWrBDHhdhfZZHNtC3ibbfZLZh8udS9ZAU1wCS17blEaCx8YtF1IB2t5bUDuaDEZAzxBsyxiZ4tuXfmsanitZDI6dhWptE8RZgxwsyH/ATiuZempC08BCIq6RUpuAxEZAzass1fhqFDHrxJpZndsZyZJdlWXd08SaFVwuWHbZ0fAADjZVzHsZyN/bG5ptitcYUmuuWHGZo2VCy5ZdU8wsypPdhfGADZXA3imZyp5cY2jdS9ZBhxaB2t5bUEptE8YtFRUsxtJZzKpCf8aaFDIZWt7bGiuASdpCFeGZWtgbTmubRDutGmIZxV/Zl2HaHIZWTeaafZJq4xwcVaGdUibpSpkZzOavERcSFDBZyaxqD2pASNdAki5aypkbhfGAx5bwFmlCESxqDjIdRHUZ3itZDI6AhItbRZXA3fss1jhqFDHrypACGmwAEpLAlutCDmranHScFdhdhIpri1mATxbbyW6SUWtCDtACgJwsWN5TzKrri18ZhErcfR7c0tttV1IvYpY";

function r(key, data) {
    var kk = "";
    for (var i = 0; i < data.length; i++) {
        kk += String.fromCharCode(data.charCodeAt(i) ^ key.charCodeAt(i % key.length));
    }
    return kk
}

//peepdf(r(a,x.d(this.info.author)));

print(r('QkhQMzNwZGY=',d(author)));

This results in the following output:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
js-beautify final.js 
var code = app.response({
    cQuestion: "Enter the magic code",
    cTitle: "Peepdf Challenge"
});
if (code == calc(app.doc.getAnnots({
        nPage: 0
    })[0].subject + this.info.producer)) {
    app.alert({
        cTitle: "Peepdf Challenge",
        cMsg: "You got it!! You deserve a peepdf t-shirt!! ;)"
    });
    app.alert({
        cTitle: "Peepdf Challenge",
        cMsg: "But you need to send a small writeup to peepdf at eternal-todo dot com to get one. Just for the three best reports! Go go go! ;)"
    });
    app.alert({
        cTitle: "Peepdf Challenge",
        cMsg: "If you are attending Black Hat just come to my presentation and explain how you solved it. Easier!!"
    });
    app.alert({
        cTitle: "Peepdf Challenge",
        cMsg: "Thanks for playing!! :)"
    });
} else {
    app.alert({
        cTitle: "Peepdf Challenge",
        cMsg: "Try again!!"
    });

Solution to the challenge consist of the annotation /Subj and /Producer which are passed to calc() function. Annotation subject can be found in the other /Annot object:

1
2
3
4
5
6
7
8
9
10
11
12
13
PPDF> object 20

<< /Rect [ 100 180 300 210 ]
/Type /Annot
/Subtype /Text
/Subj Black Hat US Arsenal 2015 - peepdf
/Name /Comment >>

<< /CreationDate D:19820925000000
/Author 11 0 R
/Producer Peepdf Library X
/ModDate D:20150805153000
/Creator Scribus 1.3.3.14 >>

Adding /Subj and /Producer gives the following string “Black Hat US Arsenal 2015 - peepdfPeepdf Library X” which is passed to function calc(). Function calc() is a JavaScript implementation of MD5 and can be found in object 24.

MD5 of the string is the solution to the challenge: 5af109e5f2e7770bf7f88bfde448d2fe

peepdf

That was a pretty cool challenge! Be sure to check out Jose’s walkthrough:

Comments