Skip to content

phonehome: does this document call out when it is opened?

The problem

A document can report when, where and by whom it was opened. A Word file with a template "attached" from a web address fetches it on open; so does a linked picture, or a field like INCLUDEPICTURE "http://...". A path like \\server\share\x.png makes Windows connect and offer the user's login hash. A PDF can run an action when it opens, when a page opens, or when it closes. An HTML email loads its pixels. That is how tracking pixels and canary tokens work.

The usual check pulls every URL-shaped string out of the file. Office files are full of XML namespace URLs and plain hyperlinks, so every file looks suspicious; and a URL in a PDF hex string or a compressed object stream is not URL-shaped bytes at all, so it is missed.

The method

Read only the places an app fetches from, and say when each fires.

  • Office: .rels relationships marked TargetMode="External" (attachedTemplate, image, oleObject, externalLinkPath...) fire on open; hyperlink is a click. Field codes are joined across Word's runs before reading INCLUDEPICTURE, INCLUDETEXT, LINK, DDEAUTO.
  • OpenDocument: xlink:href on images and objects fires; on text links it is a click.
  • PDF: index every object, including those packed in compressed object streams, then follow the triggers: /OpenAction, every /AA (page open and close, document close, save, print), the document JavaScript tree, each along its /Next chain. Dictionaries are walked as key/value pairs, because in << /S /URI /URI (x) >> a regex for /URI finds the value of /S.
  • SVG and HTML: images, scripts, frames, <link> only for the rel values a browser fetches by itself, url() and @import in styles.
  • file:///C:/... is this machine; file://host/... and UNC paths are not.
$ phonehome Downloads/
Downloads/invoice.docx  open    attachedTemplate    https://tpl.example.test/t.dotm
Downloads/menu.pdf  open    javascript  https://js.example.test/ping
$ phonehome --all report.pdf        # clicks too
$ phonehome --ignore example.com F  # trust a host and its subdomains

Exit 0 when nothing fires on open, 1 when something does, 2 when a file could not be read. The type comes from the first bytes, not the name.

Limits

It reports what a file asks for; whether the app does it depends on the app (Excel and most PDF readers ask first; mail apps block remote images). Macros, old binary Office files, RTF, encrypted PDFs and filters other than Flate are not read. Python 3, standard library only.

Code

methods/phonehome: the script, a README, and a 55-check test whose PDFs are also rewritten by qpdf into real object streams.