PJBlog恶意广告POST漏洞之测试与修复

今天在网上找了一个Vista的皮肤,赶紧给我的BLOG换上,漂亮了许多,呵呵。 最近BLOG上,广告多得吓人,特别是在文章的评论和留言本里面,真是无孔不入的广告。由于偶比较痛恨这些广告,手动删除也不是办法,整页整页的广告评论和留言,多则上百条。而且手动在管理界面删除,也只是一个治标不治本的办法。 据寡人分析[biggrin],可能是评论和留言的post环节不够强悍,导致垃圾广告有孔而入。不过这个BLOG的评论和留言都是有验证码的,评论的POST对应于blogcomm.asp文件,评论的页面,通过指向GetCode.asp,获取验证码图片,并在Session中保存该验证码,用以和用户输入的验证码比对。 那么如果要达到用程序,自动提交垃圾广告,可以想到的方法有两种: 1.用程序识别图片验证码,获取其内容,然后模拟POST。不过这个比较高难,要程序识别图片中的数字,需要图形识别的知识。 2.绕过验证码的过程,直接POST。这个要根据具体的漏洞而定。

于是随手在google上搜索了一下,PJBlog还真有这个漏洞。[eek]

具体的漏洞描述如下: blogcomm.asp文件对验证码判断不严格。 如果用户没有请求过GetCode.asp文件,那么服务器端Session里面的GetCode值为空,而用户提交的数据里面验证码也为空,这样刚好空等于空,反而通过了验证码验证。

查看blogcomm.asp源码,在Line 94行有这样的情况。

为了验证这个漏洞是否存在,我们可以自己动手测试一下,用程序模拟POST一次。就拿偶的BLOG做实验吧。首先,随便打开一个文章,启动网络抓包工具,(这里我用的是WSE–WinSocketExpert),随便敲入几个字,查看捕获下来的网络包。我们会发现,POST的数据格式是这样的:

username=Timothy&password=&Message=PostData&logID=171&action=post&submit2=%E5%8F%91%E8%A1%A8%E8%AF%84%E8%AE%BA

username 是评论者名称

password 是密码(游客不需要密码) logID 就是文章编号 Message 就是评论内容了,垃圾广告最关注的就是这个…… action 是一个隐藏的field,值为post,这个可以在页面上通过查看源码看到 submit2 后面的码,转换成中文,其实就是“发表评论”,同样参考评论页面源码

POST的结构知道了,下面我们用程序来模拟POST一下,看漏洞是否存在,测试代码如下: (请慎用此部分代码,不要用于垃圾广告软件,谢谢 [cool])

[code]private void PostFunc()
  {

      WebClient wc = new WebClient();

      string postData = "username=Timothy&password=&Message=PostData&logID=171&action=post&submit2=%E5%8F%91%E8%A1%A8%E8%AF%84%E8%AE%BA";
      try
      {
          wc.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
          wc.UploadData("https://xiaozhou.net/cooldog/blogcomm.asp", "POST", Encoding.Default.GetBytes(postData));
      }
      catch
      {
          wc.Dispose();
          return;
      }

      wc.Dispose();
  }[/code]

    运行此程序,可以在我的blog的编号171的文章评论中,看到下图:

  看来漏洞真的存在……
这里,我们可以看到POST数据结构中,logID是可以人为改变的,也就是文章编号。如果把logID作为变量自增,测试代码外面,再套一个for循环,这个程序就成了制造垃圾广告的机器了……

找到了漏洞,得赶紧补上才行。其实我们需要修改的地方,就是在blogcomm.asp。找到94行,这里的判断,由于没有对Session进行判断才导致了此漏洞的产生。
修改方法如下:
blogcomm.asp Line:94
  原代码:

IF (memName=empty or blog_validate=true) and cstr(lcase(Session(“GetCode”)))<>cstr(lcase(validate)) then

替换后的代码,增加对Session的判断:

IF (memName=empty or blog_validate=true) and (cstr(lcase(Session(“GetCode”)))<>cstr(lcase(validate)) or IsEmpty(Session(“GetCode”))) then

大功告成了……[cool]睡觉去……
支持原创技术分享,据说打赏我的人,都找到了女朋友!