参考地址
维基泄密 vault7 Scribbles
CIA机密文档追踪工具Scribbles详细分析

Scribbles,涂鸦,office文档指纹原理,ppt有警告

在word2007上的.docx插入一张图片 解压后
word/document.xml代码如下

1
2
3
<a:blip r:embed="rId4" cstate="print"/>
改为
<a:blip r:link="rId8" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"/>

在word/_rels文件夹document.xml.rels中加入指向地址

1
<Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="http://52spider.com/u/?url=https://i.ytimg.com/vi/NemvyCnUZiM/hqdefault.jpg" TargetMode="External"/>

重新zip打包成docx后 打开文档会默认请求地址,无任何提示,excel异同
在excel2007上的.xlsx插入一张图片 解压后
xl/drawingsdocument.xml代码如下

1
2
3
<a:blip r:embed="rId4" cstate="print"/>
改为
<a:blip r:link="rId8" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"/>

在xl/drawings/_rels文件夹drawing1.xml.rels中加入指向地址

1
<Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="http://52spider.com/u/?url=https://i.ytimg.com/vi/NemvyCnUZiM/hqdefault.jpg" TargetMode="External"/>

Scribbles水印的参数为
url_Scheme:协议类型,可为HTTP和HTTPS
hostServerName:发起请求的域名
hostRootPath:发起请求的域的根路径
hostSubDirs:发起请求的域的子路径
hostFileName:发起请求的文件名
hostFileExtList:发起请求的文件类型后缀
output_Directory:已嵌入水印的文件输出路径
output_WatermarkLog:输出构造水印日志

这个地址可以是文件名、文件备注、发起人、时间做的一个哈希映射地址

图片可以加载文档页眉1像素隐藏

可以在一定程度上防止文件泄露、钓鱼获取目标人ip、阅后即焚文档

以下是请求ua

Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; MSOffice 12)

分析ua
浏览器名称 IE
浏览器版本号 7.0
渲染引擎 Trident 4.0
操作系统 Windows 7

python实现参考,不保证代码可用,原理就是解压添加,不使用openxml

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
# main.py
import os
from util import ziper
from Scr_xlsx import Scr_xlsx
from Scr_docx import Scr_docx

class Scrice():
url='https://www.baidu.com'
resources='resources'

def bute(self):
ext=os.path.splitext(self.name)[1]
if ext == '.xlsx':
Scr_xlsx(self.url,self.name,self.name1,self.resources)
if ext == '.docx':
Scr_docx(self.url,self.name,self.name1,self.resources)

def build(self,name,name1):
self.name=name
self.name1=name1
ziper(name)
self.bute()
ziper(name,name1,False)

if __name__ == '__main__':
s=Scrice()
s.url='http://www.eooall.io'
s.build('Book2.xlsx','Book3.xlsx')
# s.build('Doc1.docx','Doc2.docx')
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# Scr_docx.py
import os,shutil
from util import copytree
from fleader import fleader as rq
from bs4 import BeautifulSoup

class Scr_docx():
def __init__(self,url,name,name1,resources):
self.url=url
self.name=name
self.name1=name1
self.resources=resources
self.path=os.path.splitext(name)[0]
copytree(r'./'+self.resources+'/word/word/drawings',self.path+'/word/drawings')
self.hook_Content_Types_xml()
self.hook_xl_worksheets_sheet1_xml()
self.hook_xl_worksheets__rels_sheet1_xml_rels()
self.hook_xl_drawings__rels_drawing1_xml_rels()

def hook_Content_Types_xml(self):
f=self.path+'/[Content_Types].xml'
rt=rq.rw(f)
soup = BeautifulSoup(rt,'xml')
soup.find('Types').append(soup.new_tag("Override", PartName="/word/drawings/drawing1.xml",ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"))
rq.rw(f,str(soup),aw='w')

def hook_xl_worksheets_sheet1_xml(self):
f=self.path+'/word/document.xml'
rt=rq.rw(f)
soup = BeautifulSoup(rt,'xml')
s=soup.new_tag("drawing")
s['r:id'] = 'rId10'
soup.find('w:document').append(s)
rq.rw(f,str(soup),aw='w')

def hook_xl_worksheets__rels_sheet1_xml_rels(self):
f=self.path+'/word/_rels/document.xml.rels'
if os.path.isfile(f):
rt=rq.rw(f)
soup = BeautifulSoup(rt,'xml')
soup.find('Relationships').append(soup.new_tag("Relationship",Id="rId10",Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",Target="../drawings/drawing1.xml"))
rq.rw(f,str(soup),aw='w')


def hook_xl_drawings__rels_drawing1_xml_rels(self):
f=self.path+'/word/drawings/_rels/drawing1.xml.rels'
rt=rq.rw(f)
soup = BeautifulSoup(rt,'xml')
soup.find('Relationship')['Target'] = self.url
rq.rw(f,str(soup),aw='w')
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# Scr_xlsx.py
import os,shutil
from util import copytree
from fleader import fleader as rq
from bs4 import BeautifulSoup

class Scr_xlsx():
def __init__(self,url,name,name1,resources):
self.url=url
self.name=name
self.name1=name1
self.resources=resources
self.path=os.path.splitext(name)[0]
copytree(r'./'+self.resources+'/excel/xl/drawings',self.path+'/xl/drawings')
self.hook_Content_Types_xml()
self.hook_xl_worksheets_sheet1_xml()
self.hook_xl_worksheets__rels_sheet1_xml_rels()
self.hook_xl_drawings__rels_drawing1_xml_rels()

def hook_Content_Types_xml(self):
f=self.path+'/[Content_Types].xml'
rt=rq.rw(f)
soup = BeautifulSoup(rt,'xml')
soup.find('Types').append(soup.new_tag("Override", PartName="/xl/drawings/drawing1.xml",ContentType="application/vnd.openxmlformats-officedocument.drawing+xml"))
rq.rw(f,str(soup),aw='w')

def hook_xl_worksheets_sheet1_xml(self):
f=self.path+'/xl/worksheets/sheet1.xml'
rt=rq.rw(f)
soup = BeautifulSoup(rt,'xml')
soup.find('worksheet').append(soup.new_tag("drawing"))
soup.find('drawing')['r:id'] = 'rId2'
rq.rw(f,str(soup),aw='w')

def hook_xl_worksheets__rels_sheet1_xml_rels(self):
f=self.path+'/xl/worksheets/_rels/sheet1.xml.rels'
if not os.path.isfile(f):
f_rels=self.path+'/xl/worksheets/_rels'
if not os.path.isdir(f_rels):
os.mkdir(f_rels)
shutil.copyfile(r'./'+self.resources+'/excel/xl/worksheets/_rels/sheet1.xml.rels',f)
else:
pass

def hook_xl_drawings__rels_drawing1_xml_rels(self):
f=self.path+'/xl/drawings/_rels/drawing1.xml.rels'
rt=rq.rw(f)
soup = BeautifulSoup(rt,'xml')
soup.find('Relationship')['Target'] = self.url
rq.rw(f,str(soup),aw='w')
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# util.py
import os,shutil,zipfile

def md5(*arg):
import hashlib
hl = hashlib.md5()
line=''.join(list(map(lambda x:str(x),arg)))
hl.update(line.encode(encoding='utf-8'))
return hl.hexdigest()

def ziper(name,name1='',rm=True,rp=True):
if name1=='':
rw='r'
else:
rw='w'
if rw=='r':
path=os.path.splitext(name)[0]
if os.path.isdir(path):
shutil.rmtree(path)
z = zipfile.ZipFile(name)
path=os.path.splitext(name)[0]
z.extractall(path=path)
z.close()
if rw=='w':
path=os.path.splitext(name)[0]
namemd5=md5(name)
if os.path.isdir(path):
if os.path.isdir(namemd5):
shutil.rmtree(md5(name))
shutil.move(path,namemd5)
z = zipfile.ZipFile(name1,'w')
for root, dirs, fs in os.walk(namemd5):
for f in fs:
if rp:
rpath=root.replace(namemd5,'')
else:
rpath=root.replace(namemd5,path)
pt=os.path.join(root,f)
pt1=os.path.join(rpath,f)
z.write(pt,pt1)
z.close()
if rm:
shutil.rmtree(md5(name))
else:
shutil.move(namemd5,path)

def copytree(src, dst, symlinks=False):
names = os.listdir(src)
if not os.path.isdir(dst):
os.makedirs(dst)

errors = []
for name in names:
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
copytree(srcname, dstname, symlinks)
else:
if os.path.isdir(dstname):
os.rmdir(dstname)
elif os.path.isfile(dstname):
os.remove(dstname)
shutil.copy2(srcname, dstname)
# XXX What about devices, sockets etc.?
except (IOError, os.error) as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive copytree so that we can
# continue with other files
except OSError as err:
errors.extend(err.args[0])