之前看到这篇文章
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引用该图片
打开效果: