MiniWeb是一个针对嵌入式应用而开发的微型Web Server,它占用资源少,工作效率高,可移植性好,使用C语言编写,可以单线程服务多个用户,支持动态页面生成和页面变量替换等动态Web技术,可作为静态库或动态库内嵌于其他软件中,也可作为独立的Web server运行在Windows(可使用VC进行编译和调试)和Linux上。目前它已经在一个基于MIPS的嵌入式产品中得到应用。
漏洞影响范围:
MiniWeb 0.8.19或者更早一些的版本。windows版和linux版都有,这说明如果构造不同的EXP可以得到2种系统的权限。
漏洞的作用:
可以远程执代码,浏览任意目录,查看任意文件。
漏洞的产生原因及利用方式:
我们先来看如下代码
int mwGetLocalFileName(HttpFilePath* hfp)
{
char ch;
char *p=hfp->cFilePath,*s=hfp->pchHttpPath,*upLevel=NULL;
//pchHttpPath : HTTP请求的文件类型(GET PATH HTTP/1.1)
//cFilePath = cphRootPath+pchHttpPath
hfp->pchExt=NULL;
hfp->fTailSlash=0;
if (hfp->pchRootPath) {
p+=_mwStrCopy(hfp->cFilePath,hfp->pchRootPath);
// _mwStrCopy : 复制 src 到 dsc 并且返回src的长度
// 在这里检查路径后是否包含"/"
// 如果没有,在末尾加上slash+NULL
if (*(p-1)!=SLASH) {
*p=SLASH;
*(++p)=0;
}
}
// 把 hfp->cFilePath 用 hfp->pchRootPath填充
// 这里的 "p" 指向hfp->cFilePath的末尾
// "s" 指向hfp->pchHttpPath
while ((ch=*s)
&& ch!='?' //找到 "?" 或者为 NULL 循环结束(http://site/file?arg)
&& (int)p-(int)hfp->cFilePath<sizeof(hfp->cFilePath)-1) //
{
if (ch=='%') { // 检查unicode码,比如 %2e
*(p++) = _mwDecodeCharacter(++s); // 如果是 unicode 码返回 unicode所代表的char,比如%2e就是'.'
// 避免跨越两个点“..”的目录
if (*(p-1)=='.' &&*(p-2)=='.' ) p--;
// 可见 _mwDecodeCharacter 会返回一个点 "." ,但是大家看 "ifelse-
if" 的结构,else if (ch=='.') 不能捕获到这个点,所以我们可以利用unicode码来构造双点,从而跳至任意目录。
s += 2; // 在%后面的2 个字符(数字)
} else if (ch=='/') {
*p=SLASH;
upLevel=(++p); // 保存地址到最后一个"/"
while (*(++s)=='/');// 不管多少个"//" 都continue
continue;
} else if (ch=='+') {
*(p++)=' ';// 检查空格
s++;
} else if (ch=='.') {
//#define DEFWORD(char1,char2) (char1+(char2<<8))
// WORD(16 bit) will be (8 bit char2) + (8 bit char1)
//#define GETWORD(ptrData) (*(WORD*)(ptrData))
// 如果是'./'
if (upLevel && GETWORD(s+1)==DEFWORD('.','/')) {
s+=2;
p=upLevel;// 回到最后一个"/"
} else {
*(p++)='.'; // 双点".."
hfp->pchExt=p;
while (*(++s)=='.'); //避免双点'..' 在文件中出现
}
} else {
*(p++)=*(s++); // it's normal alphabets , just copy them
}
}
if (*(p-1)==SLASH) {
p--;
hfp->fTailSlash=1;
}
*p=0;
return (int)p-(int)hfp->cFilePath;}
通过上面的代码可以看出,如果我们利用/%2e%2e/构造url就可以随意跳到任意目录访问任意文件。
例如http://xxx.xxx.xxx/%2e%2e/%2e%2e/%2e%2e/boot.ini
再来看看下面的代码
pint.h : line 76
#define MAX_REQUEST_PATH_LEN (512/*bytes*/)
#define MAX_REQUEST_SIZE (2*1024 /*bytes*/)
pint.h :line 187
#define HTTP_BUFFER_SIZE (4*1024 /*bytes*/)
typedef struct _HttpSocket{
struct _HttpSocket *next;
SOCKET socket;
int fd;
HttpRequest request;
HttpResponse response;
unsigned char *pucData;
int iDataLength;
unsigned long flags;
void* ptr;
time_t tmAcceptTime;
time_t tmExpirationTime;
int iRequestCount;
unsigned char buffer[HTTP_BUFFER_SIZE];
} HttpSocket;
记住上面的一些定义,主要注意一下大小
// p 指向buf的末尾,如果我们发送 GET 3700*A HTTP/1.1指令
// siHeaderSize ~ 3716
// p会指向 buffer[3716+4]
p=phsSocket->buffer + phsSocket->request.siHeaderSize + 4;
p=(unsigned char*)((unsigned long)p & (-4)); //保持 4-byte 一行
*p=0;
//保存请求路径
{
char *q;
int iPathLen;
// 这个过程结束后, q 会指向HTTP请求的末尾
for (q=phsSocket->request.pucPath;*q && *q!=' ';q++);
// 计算路径的长度
iPathLen=(int)q-(int)(phsSocket->request.pucPath);
if (iPathLen>=MAX_REQUEST_PATH_LEN) {
DBG("Request path too long and is stripped/n");
// 如果路径过长, 将路径的长度剥离到最大长度 MAX_REQUEST_PATH_LEN=500
iPathLen=MAX_REQUEST_PATH_LEN-1;
}
if (iPathLen>0)
// 在buffer的后面添加路径,注意现在p 指向了buffer[3720]
// buffer里只有4069-3720=376 的空闲空间
// 而且要添加512 个char 的路径到buffer的后面
// 这个时候堆溢出就发生了,buffer的空闲空间已经容纳不下512个char
memcpy(p,phsSocket->request.pucPath,iPathLen);
*(p+iPathLen)=0;
phsSocket->request.pucPath=p;
p=(unsigned char*)(((unsigned long)(p+iPathLen+4+1))&(-4));
}
现在我们又有了远程堆溢出的可能,至于shellcode和exp就自己弄吧。