IIS文件名解析漏洞扼要分析

作者: golds7n

交流: daemon[at]t00ls.net

概括: 从技术角度分析IIS6文件名解析漏洞的原理与IIS7的相关情况。

a.IIS6错误解析文件类型现象

1、当WEB目录下,文件名以 xxx.asp;xxx.xxx 来进行命名的时候,此文件将送交asp.dll解析(也就是执行脚本)

2、当WEB目录下,在访问以 xxx.asp 命名的目录下的任意文件时,此文件将送交asp.dll解析(也就是执行脚本)

通过对IIS6的核心文件类型解析相关文件的逆向后,整理出下面的核心处理代码。

//reverse code by golds7n with ida
int __thiscall Url(void *this, char *UrlStruct)
{
  void *pW3_URL_INFO; // esi@1
  int bSuccess; // eax@1
  const wchar_t *i; // eax@2
  wchar_t *wcsSlashTemp; // ebx@6
  int wcsTemp; // eax@6
  int wcs_Exten; // eax@6
  int v8; // esi@9
  int v10; // eax@11
  int v11; // ST04_4@13
  int v12; // eax@13
  int ExtenDll; // eax@19
  int Extenisa; // eax@20
  int ExtenExe; // eax@21
  int ExtenCgi; // eax@22
  int ExtenCom; // eax@23
  int ExtenMap; // eax@24
  int Entry; // [sp+Ch] [bp-148h]@6
  wchar_t *wcsMaohaoTemp; // [sp+10h] [bp-144h]@6
  unsigned int dotCount; // [sp+14h] [bp-140h]@1
  wchar_t *Str; // [sp+18h] [bp-13Ch]@3
  char *url_FileName; // [sp+1Ch] [bp-138h]@1
  char Url_FileExtenName; // [sp+20h] [bp-134h]@1
  char v25; // [sp+50h] [bp-104h]@1

 dotCount = 0;
  pW3_URL_INFO = this;
  STRU::STRU(&Url_FileExtenName, &v25, 0x100u);
  url_FileName = (char *)pW3_URL_INFO + 228;
  bSuccess = STRU::Copy((char *)pW3_URL_INFO + 228, UrlStruct);
  if ( bSuccess < 0 )
    goto SubEnd;
  for ( i = (const wchar_t *)STRU::QueryStr((char *)pW3_URL_INFO + 228); ; i = Str + 1 )
  {
    Str = _wcschr(i, '.');   ***********N1************
    if ( !Str )
      break;
    ++dotCount;
    if ( dotCount > W3_URL_INFO::sm_cMaxDots )
      break;
    bSuccess = STRU::Copy(&Url_FileExtenName, Str);
    if ( bSuccess < 0 )
      goto SubEnd;
    wcsSlashTemp = _wcschr(Str, '/'); ***********N2************
    JUMPOUT(wcsSlashTemp, 0, loc_5A63FD37);
    wcsTemp = STRU::QueryStr(&Url_FileExtenName);
    wcsMaohaoTemp = _wcschr((const wchar_t *)wcsTemp, ':');  ***********N3************
    JUMPOUT(wcsMaohaoTemp, 0, loc_5A63FD51);
    wcs_Exten = STRU::QueryStr(&Url_FileExtenName);
    __wcslwr((wchar_t *)wcs_Exten);
    if ( META_SCRIPT_MAP::FindEntry(&Url_FileExtenName, &Entry) )
    {
      *((_DWORD *)pW3_URL_INFO + 201) = Entry;
      JUMPOUT(wcsSlashTemp, 0, loc_5A63FDAD);
      STRU::Reset((char *)pW3_URL_INFO + 404);
      break;
    }
    if ( STRU::QueryCCH(&Url_FileExtenName) == 4 )
    {
      ExtenDll = STRU::QueryStr(&Url_FileExtenName);
      if ( !_wcscmp(L".dll", (const wchar_t *)ExtenDll)
        || (Extenisa = STRU::QueryStr(&Url_FileExtenName), !_wcscmp(L".isa", (const wchar_t *)Extenisa)) )
        JUMPOUT(loc_5A63FD89);
      ExtenExe = STRU::QueryStr(&Url_FileExtenName);
      if ( !_wcscmp(L".exe", (const wchar_t *)ExtenExe)
        || (ExtenCgi = STRU::QueryStr(&Url_FileExtenName), !_wcscmp(L".cgi", (const wchar_t *)ExtenCgi))
        || (ExtenCom = STRU::QueryStr(&Url_FileExtenName), !_wcscmp(L".com", (const wchar_t *)ExtenCom)) )
        JUMPOUT(loc_5A63FD89);
      ExtenMap = STRU::QueryStr(&Url_FileExtenName);
      JUMPOUT(_wcscmp(L".map", (const wchar_t *)ExtenMap), 0, loc_5A63FD7B);
    }
  }
  if ( *((_DWORD *)pW3_URL_INFO + 201)
    || (v10 = *((_DWORD *)pW3_URL_INFO + 202), v10 == 3)
    || v10 == 2
    || (v11 = *(_DWORD *)(*((_DWORD *)pW3_URL_INFO + 204) + 0xC4C),
        v12 = STRU::QueryStr(url_FileName),
        bSuccess = SelectMimeMappingForFileExt(v12, v11, (char *)pW3_URL_INFO + 756, (char *)pW3_URL_INFO + 1012),
        bSuccess >= 0) )
    v8 = 0;
  else
SubEnd:
    v8 = bSuccess;
  STRU::_STRU(&Url_FileExtenName);
  return v8;
}

上述代码中,作星号标记的是N1,N2,N3,分别检测点号,反斜杠和分号。

大概流程为:

请求 /aaa.asp;xxxx.jpg

N1:从头部查找查找 "."号,获得 .asp;xxxx.jpg

N2:查找";"号,如果有则内存截断

N3:查找"/",如果有则内存截断

最终,将保留下来 .asp 字符串,从META_SCRIPT_MAP脚本映射表里与扩展名匹配对比,并反馈给了asp.dll处理

b.IIS7是否延续了漏洞

IIS7的核心处理代码:

//reverse code by golds7n with ida
const unsigned __int16 *__stdcall MatchPathInUrl(const unsigned __int16 *url_User, unsigned __int32 url_Length, const unsigned __int16 *IIS_MAP_Wizard)
{
  const unsigned __int16 *p; // ebx@1
  const unsigned __int16 *pUrl; // ecx@4
  const wchar_t *i; // edi@6
  signed int isXingHao; // edx@8
  const unsigned __int16 cWizard; // ax@10
  const unsigned __int16 *pWizard; // esi@11
  int cTemp; // eax@17
  int pCharTemp; // esi@23
  const unsigned __int16 *pCharUser; // eax@43
  const unsigned __int16 byteChar; // cx@44
  const wchar_t cSlash; // ax@50
  const unsigned __int16 *Str2; // [sp+8h] [bp-8h]@11
  signed int bFound; // [sp+Ch] [bp-4h]@3

 p = IIS_MAP_Wizard;
  if ( *IIS_MAP_Wizard != '*' || IIS_MAP_Wizard[1] )
  {
    bFound = 1;
    if ( *IIS_MAP_Wizard == '/' )
    {
      p = IIS_MAP_Wizard + 1;
      bFound = 0;
      ++IIS_MAP_Wizard;
    }
    pUrl = url_User;
    if ( *url_User == '/' )
    {
      pUrl = url_User + 1;
      ++url_User;
    }
LABEL_6:
    for ( i = pUrl; ; i += pCharTemp )
    {
      while ( *p == '?' )
      {
        if ( !*i )
          return 0;
        if ( *i == '/' )
          goto LABEL_30;
        ++p;
        ++i;
      }
      isXingHao = 0;
      if ( *p == '*' )
      {
        ++p;
        isXingHao = 1;
      }
      cWizard = *p;
      if ( !*p )
        break;
      pWizard = p;
      Str2 = p;
      if ( cWizard != '*' )
      {
        do
        {
          if ( cWizard == '?' )
            break;
          if ( !cWizard )
            break;
          ++pWizard;
          cWizard = *pWizard;
          Str2 = pWizard;
        }
        while ( *pWizard != '*' );
      }
      if ( isXingHao )
      {
        if ( !*pWizard )
        {
          cTemp = (int)&i[pWizard - p];
          if ( cTemp > (unsigned int)&pUrl[url_Length] )
            return 0;
          while ( *(_WORD *)cTemp != '/' && *(_WORD *)cTemp && *i != '/' && *i )
          {
            ++i;
            cTemp += 2;
          }
        }
        pCharTemp = pWizard - p;
        while ( _wcsncmp(i, p, pCharTemp) )
        {
          if ( !*i )
            return 0;
          if ( *i == '/' )
            goto LABEL_29;
          ++i;
        }
      }
      else
      {
        pCharTemp = pWizard - p;
        if ( _wcsncmp(i, p, pCharTemp) )
        {
LABEL_29:
          pUrl = url_User;
LABEL_30:
          if ( !bFound )
            return 0;
          while ( *pUrl != '/' )
          {
            if ( !*pUrl )
              return 0;
            ++pUrl;
          }
          if ( !*pUrl )
            return 0;
          p = IIS_MAP_Wizard;
          ++pUrl;
          url_User = pUrl;
          goto LABEL_6;
        }
      }
      p = Str2;
      pUrl = url_User;
    }
    if ( isXingHao )
    {
      cSlash = *i;
      if ( *i == '/' )
        return i;
      do
      {
        if ( !cSlash )
          break;
        ++i;
        cSlash = *i;
      }
      while ( *i != '/' );
    }
    if ( *i != '/' && *i )
      goto LABEL_30;
    return i;
  }
  pCharUser = url_User;
  do
  {
    byteChar = *pCharUser;
    ++pCharUser;
  }
  while ( byteChar );
  return &url_User[pCharUser - (url_User + 1)];
}

MatchPathInUrl(const unsigned __int16 *url_User, unsigned __int32 url_Length, const unsigned __int16 *IIS_MAP_Wizard)

参数url_User是用户提交的路径参数,类似PHOST/DEFAULT WEB SITE/aa.asp;xxx.jpg,由 服务/站点名称/请求路径 构成,IIS_MAP_Wizard是在管理器文件映射里的每个表项,譬如*.ASP

比较的结果就是,拿aa.asp;xxx.jpg与*.ASP进行匹配,显然结果是不匹配的(/xxx.asp/xxx.jpg,是拿xxx.jpg和*.ASP进行匹配)。

c.总结

IIS6文件映射配置图

IIS 文件名解析漏洞扼要分析

IIS7文件映射配置图

IIS 文件名解析漏洞扼要分析

从上面的关键分析和图中,可以看出,IIS6存在解析漏洞是由于其设计上的问题导致,IIS6只简单地根据扩展名来识别,而IIS7是进行通配符匹配来定夺请求文件是否是某脚本类型,可见IIS7纠正了错误机制,更加科学、健壮。