Supesite 各种注入组合。
小菜刚来t00ls 啥权限都没。。 发个帖子挣点权限。。
其他板块没啥权限 发这来了。
已经wooyun过了 不过supesite 早就停止维护了 也就没补丁。
select update insert delete && 二次注入。
_______________________________________________________________________________
0x01 update注入 可提升自己为管理
在index.php中这里包含文件进来。 来看看$_SGET 怎么来的。这里用/来切割成数组 然后就return 。
在viewcomment.php中这里 我们来看这里 UPDATE '.tname($table_name).首先先注册一个会员
这里我们先随便找一个新闻页面 然后自己评论一下
'SELECT * FROM '.tname('spacecomments').' WHERE cid=\''.$cid.'\''
这里由于我们只能查看自己的评论
这里怎么查看自己评论的cid呢?
首先自己评论一下
然后
点一下引用 然后抓下包 就能看到 我们发的这个评论的cid为7
然后构造一下参数 在index.php中 把viewcomments.php中包含进来
成功报错
可以看到如果查看的不是自己发的评论 就出错额。
因为是update 所以自己update自己的groupid 为1 即可直接提升自己为管理。
http://127.0.0.1/dan/supesite/space.php?uid=13
首先进一下我们的个人主页 可以看到uid为13
然后构造一下语句
没报错了 语句成功执行
这里语句成功执行后
$_SGLOBAL['db']->query('DELETE FROM '.tname('spacecomments').' WHERE cid=\''.$cid.'\'');
就会执行这delete了。 就会删除这评论了 不过权限已提升了
提升权限 成功进后台
_________________________________________________________________________________
0x02 insert注入
先来看看全局文件判断gpc 是否开启 如果没有开启 就对get post cookie 转义
这里没有对files转义。
在batch.upload.php中'filename' => saddslashes($filearr['name']) 在查询的时候名字被转义了
'attachtype' => $fileext 来看一下$fileext
$fileext = fileext($filearr['name']);获取点以后的 没做转义 所以可以在后缀这进行注入了。
可以看到 名字被转义 后缀那成功引入单引号
这里uploadallowtype那里乱写一个
成功出数据。
_____________________________________________________________________________
0x03 delete注入
在cp.php中包含进来
在source/cp_news.php中这里主要是出来一个小的设计缺陷。
这里把 $query = $_SGLOBAL['db']->query('SELECT * FROM '.tname('spacetags').' WHERE itemid=\''.$itemid.'\' AND status=\''.$status.'\'')
$itemid 首先带入到了查询当中 是被单引号了的。。 如果查询出来的有结果 才会带入到delete中 如果无结果 就不执行delete的语句了。
而在数据库中$itemid中 存储的是int类型 所以他这里本来是想要用户只能提交数字型才能查询出结果。
但是由于mysql的类型转换 因为他这里储存的是int类型 所以我们提交4xxxxx 跟我们提交4 是一样的
首先我们注册一个会员 然后投稿
投稿 这里tag 随便写一个
+--------+-------+------------+------+--------+
| itemid | tagid | dateline | type | status |
+--------+-------+------------+------+--------+
| 3 | 1 | 1412680532 | news | 0 |
| 4 | 2 | 1412680930 | news | 0 |
数据库里也就创建了。。
这里的itemid 在http://127.0.0.1/dan/supesite/cp.php?ac=news&op=view&itemid=4
地址中就能看到为4
然后在$query = $_SGLOBAL['db']->query('SELECT * FROM '.tname('spacetags').' WHERE itemid=\''.$itemid.'\' AND status=\''.$status.'\'');
这里查询
这里查询 后面虽然跟了一些字符 提示warning 但是还是能查询出来。
$_SGLOBAL['db']->query('DELETE FROM '.tname('spacetags').' WHERE itemid='.$itemid.' AND tagid IN ('.simplode($deletetagidarr).') AND status=\''.$status.'\'');
然后就进来delete 里面没单引号 且无intval 导致注入。
投稿的时候抓包一下
成功出数据
______________________________________________________________________
0x04 select注入
batch.common.php中像其他文件中的 例如 comment.php里面的 $_GET[name]都做正则了 而这个没有做正则。 导致了注入
成功出数据。
_______________________________________________________________
0x05 鸡肋的二次注入
在cp.php中包含文件进来
在source/cp_news.php中这里对投稿时的处理。全局对_POST转义了 然后转义入库 转义符就没了。
找找出库的地方。
在viewcomment.php中$channel = $type = empty($item['type']) ? $type : $item['type'];
赋值的是查询出来的 就能引入单引号了。再把出库的带入到了查询当中
造成了注入。
在viewnews.php中 也有出库的注册一个会员 然后发帖
入库
因为数据库存储的有长度限制 所以鸡肋。
其他板块没啥权限 发这来了。
已经wooyun过了 不过supesite 早就停止维护了 也就没补丁。
select update insert delete && 二次注入。
_______________________________________________________________________________
0x01 update注入 可提升自己为管理
在index.php中
if($_SGET['action'] != 'index') {
if(empty($channels['menus'][$_SGET['action']]['upnameid']) && $channels['menus'][$_SGET['action']]['upnameid'] != 'news') {
$scriptfile = S_ROOT.'./'.$_SGET['action'].'.php';
} else {
$scriptfile = S_ROOT.'./news.php';
}
if(empty($parsegetvar)) {
$parsegetvar = empty($_SERVER['QUERY_STRING'])?'':$_SERVER['QUERY_STRING'];
}
if(!empty($parsegetvar)) {
$parsegetvar = addslashes($parsegetvar); //这里转义了
$_SGET = parseparameter(str_replace(array('-','_'), '/', $parsegetvar)); // 这里 注册了这变量
}
function parseparameter($param, $nofix=1) {
global $_SCONFIG;
$paramarr = array();
if($nofix && !empty($_SCONFIG['pagepostfix'])) {
if(strrpos($param, $_SCONFIG['pagepostfix'])) {
$param = substr($param, 0, strrpos($param, $_SCONFIG['pagepostfix']));
}
}
$sarr = explode('/', $param);
if(empty($sarr)) return $paramarr;
if(is_numeric($sarr[0])) $sarr = array_merge(array('uid'), $sarr);
if(count($sarr)%2 != 0) $sarr = array_slice($sarr, 0, -1);
for($i=0; $i<count($sarr); $i=$i+2) {
if(!empty($sarr[$i+1])) $paramarr[$sarr[$i]] = addslashes(str_replace(array('/', '\\'), '', rawurldecode(stripslashes($sarr[$i+1]))));
}
return $paramarr;
}
在viewcomment.php中
if(!empty($_SGET['op']) && $_SGET['op'] == 'delete') {
$cid = empty($_SGET['cid'])?0:intval($_SGET['cid']);
$ismodle = empty($_SGET['ismodle']) ? 0 : intval($_SGET['ismodle']);
$table_name = ( $ismodle && !empty($_SGET['type']) ? $_SGET['type'] : 'space' ).'items';
if(empty($cid)) showmessage('not_found', S_URL);
$itemid = empty($_SGET['itemid'])?0:intval($_SGET['itemid']);
if(empty($itemid)) showmessage('not_found', S_URL);
$deleteflag = false;
if(empty($_SGLOBAL['group'])) {
showmessage('no_permission');
}
if($cid && $itemid && $_SGLOBAL['supe_uid']) {
$query = $_SGLOBAL['db']->query('SELECT * FROM '.tname('spacecomments').' WHERE cid=\''.$cid.'\'');
if($comment = $_SGLOBAL['db']->fetch_array($query)) {
if($_SGLOBAL['group']['groupid'] == 1 || $comment['authorid'] == $_SGLOBAL['supe_uid']) {
$_SGLOBAL['db']->query('UPDATE '.tname($table_name).' SET replynum=replynum-1 WHERE itemid=\''.$comment['itemid'].'\'');
$_SGLOBAL['db']->query('DELETE FROM '.tname('spacecomments').' WHERE cid=\''.$cid.'\'');
$ismodle = empty($_SGET['ismodle']) ? 0 : intval($_SGET['ismodle']);
$table_name = ( $ismodle && !empty($_SGET['type']) ? $_SGET['type'] : 'space' ).'items'; //这里如果$ismodle为true的话 那么$table_name 就为$_SGET['type'] 刚好$ismodle 也是$_SGET来的 在结合index.php里的注册$_SGET 那么则table_name可控了
if($cid && $itemid && $_SGLOBAL['supe_uid']) { //这里cid 和 itemid 都是index.php中注册的$_SGET来的 $_SGLOBAL['supe_uid'] 这个只要登录了用户就行
$query = $_SGLOBAL['db']->query('SELECT * FROM '.tname('spacecomments').' WHERE cid=\''.$cid.'\'');//把评论查询出来
if($comment = $_SGLOBAL['db']->fetch_array($query)) {
if($_SGLOBAL['group']['groupid'] == 1 || $comment['authorid'] == $_SGLOBAL['supe_uid'])//这里必须是管理员 或者 你查看的是自己的评论 那么我们就自己评论一个
{
$_SGLOBAL['db']->query('UPDATE '.tname($table_name).' SET replynum=replynum-1 WHERE itemid=\''.$comment['itemid'].'\'')//table_name可控 注入;
这里我们先随便找一个新闻页面 然后自己评论一下
'SELECT * FROM '.tname('spacecomments').' WHERE cid=\''.$cid.'\''
这里由于我们只能查看自己的评论
这里怎么查看自己评论的cid呢?
首先自己评论一下
然后
点一下引用 然后抓下包 就能看到 我们发的这个评论的cid为7
然后构造一下参数 在index.php中 把viewcomments.php中包含进来
成功报错
可以看到如果查看的不是自己发的评论 就出错额。
因为是update 所以自己update自己的groupid 为1 即可直接提升自己为管理。
http://127.0.0.1/dan/supesite/space.php?uid=13
首先进一下我们的个人主页 可以看到uid为13
然后构造一下语句
没报错了 语句成功执行
这里语句成功执行后
$_SGLOBAL['db']->query('DELETE FROM '.tname('spacecomments').' WHERE cid=\''.$cid.'\'');
就会执行这delete了。 就会删除这评论了 不过权限已提升了
提升权限 成功进后台
_________________________________________________________________________________
0x02 insert注入
先来看看全局文件
if(!(get_magic_quotes_gpc())) {
$_GET = saddslashes($_GET);
$_POST = saddslashes($_POST);
$_COOKIE = saddslashes($_COOKIE);
}
这里没有对files转义。
在batch.upload.php中
elseif (!empty($_POST)) { //如果POST不为空
//编辑标题
if(!empty($_GET['editaid']) && $editaid = intval($_GET['editaid'])) {
$editsubject = cutstr(trim(shtmlspecialchars($_POST['editsubject'])), 50);
updatetable('attachments', array('subject'=>$editsubject), array('aid'=>$editaid));
print <<<END
<script language="javascript">
var div = parent.document.getElementById("div_upload_" + $editaid);
var pf = parent.document.getElementById("phpframe");
pf.src = "about:blank";
div.innerHTML = "$editsubject";
</script>
END;
exit;
}
//上传文件
//上传模式
$mode = intval(postget('mode'));
if($mode>3) exit; //mode 直接让他为空
$hash = trim(preg_replace("/[^a-z0-9\-\_]/i", '', trim($_POST['hash'])));
if(strlen($hash) != 16) showresult($blang['unable_to_complete_this_craft']);//这里只判断hash的长度为不为16 没有进一步的验证 那么就让hash为1111111111111111
//个数
$filecount = 1;
$query = $_SGLOBAL['db']->query('SELECT COUNT(*) FROM '.tname('attachments').' WHERE hash=\''.$hash.'\'');
$count = $_SGLOBAL['db']->result($query, 0);
$allowmax = intval($_POST['uploadallowmax']);
if($allowmax > 0 && $count + $filecount > $allowmax) showresult($blang['the_number_has_reached_maximum']);
//类型
$allowtypearr = getallowtype(trim($_POST['uploadallowtype']));//取得上传的类型
//空间
$attachsize = 0;
include_once(S_ROOT.'./function/upload.func.php');
if(empty($mode)) { //让$mode为空即可
//本地上传
//检查
$filearr = $_FILES['localfile'];//获取files
if(empty($filearr['size']) || empty($filearr['tmp_name'])) showresult($blang['failure_to_obtain_upload_file_size']);
$fileext = fileext($filearr['name']);//获取后缀
if(!empty($allowtypearr)) {
if(empty($allowtypearr[$fileext])) showresult($blang['upload_not_allow_this_type_of_resources']." ($allowtype_ext)");
if($filearr['size'] > $allowtypearr[$fileext]['maxsize']) showresult($blang['file_size_exceeded_the_permissible_scope']);
}
//缩略图
if(!empty($_POST['uploadthumb0']) && !empty($_SCONFIG['thumbarray'][$_POST['uploadthumb0']])) {
$thumbarr = $_SCONFIG['thumbarray'][$_POST['uploadthumb0']];
} else {
$thumbarr = array($_POST['thumbwidth'], $_POST['thumbheight']);
}
//上传
$newfilearr = savelocalfile($filearr, $thumbarr);
if(empty($newfilearr['file'])) showresult($blang['uploading_files_failure']);
//数据库
if(empty($_POST['uploadsubject0'])) $_POST['uploadsubject0'] = cutstr(filemain($filearr['name']), 50);
//下面就带入到insert当中啦
$insertsqlarr = array(
'uid' => $uid,
'dateline' => $_SGLOBAL['timestamp'],
'filename' => saddslashes($filearr['name']),//对文件的名字转义
'subject' => trim(shtmlspecialchars($_POST['uploadsubject0'])),
'attachtype' => $fileext,//这里没有对文件的后缀转义
'isimage' => (in_array($fileext, array('jpg','jpeg','gif','png'))?1:0),
'size' => $filearr['size'],
'filepath' => $newfilearr['file'],
'thumbpath' => $newfilearr['thumb'],
'hash' => $hash
);
inserttable('attachments', $insertsqlarr)//insert;
'attachtype' => $fileext 来看一下$fileext
$fileext = fileext($filearr['name']);
function fileext($filename) {
return strtolower(trim(substr(strrchr($filename, '.'), 1)));
}
可以看到 名字被转义 后缀那成功引入单引号
这里uploadallowtype那里乱写一个
成功出数据。
_____________________________________________________________________________
0x03 delete注入
在cp.php中
$ac = empty($_GET['ac']) ? 'profile' : trim($_GET['ac']);
if(in_array($ac, array('index', 'news', 'profile', 'credit', 'models'))) {
include_once(S_ROOT.'./source/cp_'.$ac.'.php');
在source/cp_news.php中
if(empty($itemid)) { //这里让$itemid 不为空
if(!empty($_SCONFIG['posttime']) && $_SGLOBAL['group']['groupid'] != 1) {
if($_SGLOBAL['timestamp'] - $_SGLOBAL['member']['lastposttime'] < $_SCONFIG['posttime']) {
showmessage('post_too_much');
}
}
$newsarr['uid'] = $_SGLOBAL['supe_uid'];
$newsarr['username'] = $_SGLOBAL['supe_username'];
$newsarr['dateline'] = $_SGLOBAL['timestamp'];
if($_POST['fromtype'] == 'newspost') {
$newsarr['fromtype'] = 'newspost';
$newsarr['fromid'] = intval($_POST['id']);
} else {
$newsarr['fromtype'] = 'userpost';
}
if(!checkperm('allowdirectpost')) {
$itemarr['itemid'] = inserttable('spaceitems', $newsarr, 1);
inserttable('spacenews', $itemarr);
getreward('postinfo');
postspacetag('add', $_POST['type'], $itemarr['itemid'], $tagarr,1);
$do = 'pass';
} else {
$itemarr['itemid'] = inserttable('postitems', $newsarr, 1);
inserttable('postmessages', $itemarr);
postspacetag('add', $_POST['type'], $itemarr['itemid'], $tagarr,0);
$do = 'me';
}
//更新用户最新更新时间
if($_SGLOBAL['supe_uid']) {
updatetable('members', array('updatetime'=>$_SGLOBAL['timestamp'], 'lastposttime'=>$_SGLOBAL['timestamp']), array('uid'=>$_SGLOBAL['supe_uid']));
}
} else { //进入else
if(empty($_SGLOBAL['supe_uid'])) showmessage('no_permission');
updatetable('postitems', $newsarr, array('itemid'=>$itemid));
updatetable('postmessages', $itemarr, array('itemid'=>$itemid));
$itemid = empty($_POST['oitemid']) ? $itemid : $_POST['oitemid'];//没有intval
postspacetag('update', $_POST['type'], $itemid, $tagarr, 0);//跟这里
}
function postspacetag($op, $type, $itemid, $tagarr, $status) {
global $_SGLOBAL;
$deletetagidarr = $addtagidarr = $spacetagidarr = array();
if($op == 'add') { //已经存在的tag,执行加入操作
if(!empty($tagarr['existsid'])) {
$addtagidarr = $tagarr['existsid'];
$_SGLOBAL['db']->query('UPDATE '.tname('tags').' SET spacenewsnum=spacenewsnum+1 WHERE tagid IN ('.simplode($tagarr['existsid']).')');
}
} else {
$query = $_SGLOBAL['db']->query('SELECT * FROM '.tname('spacetags').' WHERE itemid=\''.$itemid.'\' AND status=\''.$status.'\'');//查询
while ($spacetag = $_SGLOBAL['db']->fetch_array($query)) {
if(!empty($tagarr['existsid']) && in_array($spacetag['tagid'], $tagarr['existsid'])) {
$spacetagidarr[] = $spacetag['tagid'];
} else {
$deletetagidarr[] = $spacetag['tagid'];//赋值
}
}
foreach ($tagarr['existsid'] as $etagid) {
if(!empty($spacetagidarr) && in_array($etagid, $spacetagidarr)) {
} else {
$addtagidarr[] = $etagid;
}
}
if(!empty($deletetagidarr)) {
//这里要$deletetagidarr不为空
那么也就是要让
$query = $_SGLOBAL['db']->query('SELECT * FROM '.tname('spacetags').' WHERE itemid=\''.$itemid.'\' AND status=\''.$status.'\'')这个查询出来的有内容
$_SGLOBAL['db']->query('DELETE FROM '.tname('spacetags').' WHERE itemid='.$itemid.' AND tagid IN ('.simplode($deletetagidarr).') AND status=\''.$status.'\'');//这里delete查询 WHERE itemid='.$itemid.' 没有被单引号引住。。 并且没intval导致注入
$_SGLOBAL['db']->query('UPDATE '.tname('tags').' SET spacenewsnum=spacenewsnum-1 WHERE tagid IN ('.simplode($deletetagidarr).')');
}
这里把 $query = $_SGLOBAL['db']->query('SELECT * FROM '.tname('spacetags').' WHERE itemid=\''.$itemid.'\' AND status=\''.$status.'\'')
$itemid 首先带入到了查询当中 是被单引号了的。。 如果查询出来的有结果 才会带入到delete中 如果无结果 就不执行delete的语句了。
而在数据库中$itemid中 存储的是int类型 所以他这里本来是想要用户只能提交数字型才能查询出结果。
但是由于mysql的类型转换 因为他这里储存的是int类型 所以我们提交4xxxxx 跟我们提交4 是一样的
首先我们注册一个会员 然后投稿
投稿 这里tag 随便写一个
+--------+-------+------------+------+--------+
| itemid | tagid | dateline | type | status |
+--------+-------+------------+------+--------+
| 3 | 1 | 1412680532 | news | 0 |
| 4 | 2 | 1412680930 | news | 0 |
数据库里也就创建了。。
这里的itemid 在http://127.0.0.1/dan/supesite/cp.php?ac=news&op=view&itemid=4
地址中就能看到为4
然后在$query = $_SGLOBAL['db']->query('SELECT * FROM '.tname('spacetags').' WHERE itemid=\''.$itemid.'\' AND status=\''.$status.'\'');
这里查询
这里查询 后面虽然跟了一些字符 提示warning 但是还是能查询出来。
$_SGLOBAL['db']->query('DELETE FROM '.tname('spacetags').' WHERE itemid='.$itemid.' AND tagid IN ('.simplode($deletetagidarr).') AND status=\''.$status.'\'');
然后就进来delete 里面没单引号 且无intval 导致注入。
投稿的时候抓包一下
成功出数据
______________________________________________________________________
0x04 select注入
batch.common.php中
$name = empty($_GET['name'])?'':trim($_GET['name']);
$cid = empty($_GET['cid'])?0:intval($_GET['cid']);
$html = false;
if(!empty($name) && !empty($cid)) {
$item = array();
$query = $_SGLOBAL['db']->query('SELECT * FROM '.tname($name.'comments').' WHERE cid=\''.$cid.'\'');
成功出数据。
_______________________________________________________________
0x05 鸡肋的二次注入
在cp.php中
$ac = empty($_GET['ac']) ? 'profile' : trim($_GET['ac']);
if(in_array($ac, array('index', 'news', 'profile', 'credit', 'models'))) {
include_once(S_ROOT.'./source/cp_'.$ac.'.php');
在source/cp_news.php中
$newsarr = array('subject' => $_POST['subject'],
'catid' => $_POST['catid'],
'type' => $_POST['type'],
'lastpost' => $_SGLOBAL['timestamp']);
$itemarr = array('message' => $_POST['message'],
'relativetags' => addslashes(serialize($tagnamearr)), //相关TAG
'newsfrom' => $_POST['newsfrom'],
'newsauthor' => $_POST['newsauthor'],
'newsfromurl' => $_POST['newsfromurl'],
'postip' => $_SGLOBAL['onlineip'],
'includetags' => postgetincludetags($_POST['message'], $tagnamearr)
);
$itemarr['itemid'] = inserttable('spaceitems', $newsarr, 1); //这里
inserttable('spacenews', $itemarr);
getreward('postinfo');
postspacetag('add', $_POST['type'], $itemarr['itemid'], $tagarr,1);
$do = 'pass';
} else {
$itemarr['itemid'] = inserttable('postitems', $newsarr, 1);
找找出库的地方。
在viewcomment.php中
if($channels['menus'][$type]['type'] == 'model') {
include_once(S_ROOT.'./function/model.func.php');
$cacheinfo = getmodelinfoall('modelname', $type);
if(empty($cacheinfo['models'])) {
showmessage('visit_the_channel_does_not_exist', S_URL);
}
$modelsinfoarr = $cacheinfo['models'];
$categories = $cacheinfo['categories'];
$query = $_SGLOBAL['db']->query('SELECT i.*, ii.* FROM '.tname($type.'items').' i, '.tname($type.'message').' ii WHERE i.itemid = ii.itemid AND i.itemid=\''.$itemid.'\' AND i.allowreply=\'1\'');
$ismodle = '1';
} else {
$query = $_SGLOBAL['db']->query('SELECT i.*, ii.* FROM '.tname('spaceitems').' i, '.tname('spacenews').' ii WHERE i.itemid = ii.itemid AND i.itemid=\''.$itemid.'\' AND i.allowreply=\'1\'');//这里查询出来
$ismodle = '0';
}
if(!$item = $_SGLOBAL['db']->fetch_array($query)) showmessage('not_found', S_URL)//出库;
赋值的是查询出来的 就能引入单引号了。
$sql = "SELECT COUNT(*) FROM ".tname('spacecomments')." WHERE itemid='$itemid' AND status='1' AND `type`='$type' $wherestr ";
$listcount = $_SGLOBAL['db']->result($_SGLOBAL['db']->query($sql), 0);
$iarr = array();
造成了注入。
在viewnews.php中 也有出库的
if(!empty($_SCONFIG['viewspace_pernum']) && $listcount) {
$repeatids = array();
$j = 1;
$sql = "SELECT c.* FROM ".tname('spacecomments')." c WHERE c.itemid='$news[itemid]' AND c.type='$news[type]' AND status='1' ORDER BY c.dateline ".($_SCONFIG['commorderby']?'DESC':'ASC')." LIMIT 0, $_SCONFIG[viewspace_pernum]";
$query = $_SGLOBAL['db']->query($sql);
入库
因为数据库存储的有长度限制 所以鸡肋。
评论16次
good job!谢谢楼主!
很好的一篇 教学文章
现在年轻人都这么牛掰
很详细,先收藏了
非常详细啊。。。。。
看过。god
源码审计牛,收藏学xi
非常详细的注入组合,辛苦楼主了!
现在的年轻人越来越凶残了
路过来学xi了,
原来雨牛也来土司了
真心强大,雨牛
欢迎雨牛入驻土司!
http://www.wooyun.org/whitehats/′雨。
非常详细的注入组合,辛苦楼主了!
太长了,看的人内啥都疼了。。