之前看到这篇文章

https://medium.com/@codedbrain/hiding-js-in-a-jpeg-header-454386f9e20

自己实际操作了一下,碰到了不少问题,特此记录一下。

jpg文件以FF D8开头,以FF D9结束,中间包含以FF XX(X表示十六进制数)开头的不同块。

常见的jpg文件在FF D8开头后,是一个以FF E0开头的块,其后的00 10表示长度(包含表示长度本身的这两字节)

该块具体各项含义如下

言归正传,大体的操作就是把alert(1);插入到ffe0这个块中,在下一个块ff db之前。

直接插入肯定不会是合法的js文件,所以可以使用注释符/**/来把其他东西注释掉。

首先用2F 2A来替换前面提到的长度00 10,2F 2A是/ *的十六进制ascii

那么问题来了,为了使这个图片还是合法的jpg图片,那么这个块的长度也要有2F 2A字节,即12074字节,所以要额外填充很多字节。

具体有12074 = 16(这个块原本的大小) + 插入的js语句字节数(比如 */=alert(1);/* 是14个字节) + 填充字节(可以填00)

将上述字节插入到FF DB前面后,再在最后的FF D9后插入一个2A 2F(*/)闭合前面的注释符即可。

最终变成了前面的一串乱码(FFD8...)作为变量名 = alert(1);这样的可执行语句。

附带写的转化脚本以及使用的原图

脚本:

import binascii

ff = open('test.jpg', 'rb')

f = ff.read()
ff.close()

jpgHex = binascii.b2a_hex(f).decode('utf-8')

c = jpgHex[0 : 8] + '2f2a' + jpgHex[12:40]

e = binascii.b2a_hex('*/=alert(1);/*'.encode('utf-8')).decode('utf-8')

d = '00' * (12058 - len(e) // 2)

f = jpgHex[40 :]

g = c + d + e + f + '2a2f'

new = binascii.a2b_hex(g)

ff = open('new.jpg', 'wb+')

ff.write(new)

ff.close()

原图生成(1X1的图片):

from PIL import Image

im = Image.new("RGB", (1, 1))

im.putpixel((0, 0), (255, 255, 255))

im.save('flag.jpg')

使用这种图片是因为在之前的尝试过程中发现有的图片无法成功,猜测是图片中包含了一些字符被js解析导致语法错误,所以这样生成简单图片减小这种可能性。

对应的html引用该图片

打开效果:

打赏作者