前言 暑假简单的学了一下代码审计,整几个简单的cms来练习
一、sql注入一般审计方法 1.数据库监控 通过黑盒功能点测试,查看sql语句,在通过SQL语句关键字进行代码审计 2.关键函数审计 通过审计关键函数的代码来审计漏洞 正则表达式 :(update|select|insert|delete|).?where. = 静态代码自动审计
二、审计练习 1.bluecms(无过滤) 使用正则表达式:(update|select|insert|delete|).?where. =
选择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 30 31 32 33 34 35 36 37 38 39 40 41 42 <?php 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" ; ?>
发现通过get方式接受ad_id变量没有任何过滤拼接到sql语句并执行
1 2 3 4 5 6 7 $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);
定位getone函数,直接执行了语句
1 2 3 4 5 6 function getone ($sql, $type=MYSQL_ASSOC) { $query = $this->query($sql,$this->linkid); $row = mysql_fetch_array($query, $type); return $row; }
2.lmxcms 1.4(无过滤)mvc架构 cnvd 从cnvd发现梦想CMS后台存在SQL注入,知道Bo、cl开头的文件
从描述知道是后台sql注入判断在c/admin/ 文件夹找Bo开头文件 在文件发现只有reply函数有变量传参操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public function reply () { $id = $_GET['id' ] ? $_GET['id' ] : $_POST['id' ]; $reply = $this->bookModel->getReply(array ($id)); if ($reply){ $reply = string ::html_char($reply[0 ]['content' ]); $this->smarty->assign('content' ,$reply); $this->smarty->assign('type' ,'update' ); }else { $this->smarty->assign('type' ,'add' ); } if (isset($_POST['reply' ])){ if (!$_POST['content' ]){ rewrite::js_back('回复内容不能为空' ); } $this->bookModel->reply(array ('id' =>$id,'type' =>$_POST['type' ],'username' =>$this->username)); addlog('留言回复【id:' .$_POST['id' ].' 】'); rewrite::succ(' 修改成功',' ?m=Book' ); } $this->smarty->assign('id' ,$id); $this->smarty->display('Book/reply.html' ); } }
发现通过get或post对id传参到getReply函数,定位getReply函数
1 2 3 4 5 6 public function getReply (array $id) { $id = implode(',' ,$id); $param['where' ] = 'uid in(' .$id.' )'; return parent::selectModel($param); }
继续定位selectModel函数
1 2 3 4 5 6 7 protected function selectModel ($param=array ()) { if ($param['field' ]){ $this->field=$param['field' ]; } return parent::selectDB($this->tab['0' ],$this->field,$param); }
继续定位selectDB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 protected function selectDB ($tab,Array $field,$param=array ()) { $arr = array (); $field = implode(',' ,$field); $force = ''; //强制进入某个索引 if($param[' force' ]) $force = ' force index(' .$param['force' ].' )'; if($param[' ignore' ]) $force = ' ignore index(' .$param['ignore' ].' )'; $sqlStr = $this->where($param); $sql="SELECT $field FROM ".DB_PRE."$tab$force $sqlStr"; $result=$this->query($sql); while(!!$a=mysql_fetch_assoc($result)){ $arr[]=$a; } $this->result($result); return $arr; }
发现变量$sqlStr拼接在sql语句并执行
构造报错注入paylad:http://127.0.0.1/lmxcms1.4/admin.php?m=book&a=reply&id=1)%20 and%20updatexml(0,concat(0x7e,user()),1)%23 因为是mvc架构m是功能也就是文件名a是函数在拼接参数
2.LmxCMS V1.4 前台 Ta***.cl***.php 存在 SQL 注入漏洞(有过滤)mvc架构 查找ta开头文件,观察发现data变量经过p函数
1 2 3 4 5 6 7 8 9 10 11 public function __construct() { parent::__construct(); $data = p(2 ,1 ,1 ); $name = string ::delHtml($data['name' ]); if (!$name) _404(); $name = urldecode($name); if ($this->tagsModel == null) $this->tagsModel = new TagsModel(); $this->data = $this->tagsModel->getNameData($name); if (!$this->data) _404(); }
定义p函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 function p ($type=1 ,$pe=false ,$sql=false ,$mysql=false ) { if ($type == 1 ){ $data = $_POST; }else if ($type == 2 ){ $data = $_GET; }else { $data = $type; } if ($sql) filter_sql($data); if ($mysql) mysql_retain($data); foreach($data as $k => $v){ if (is_array($v)){ $newdata[$k] = p($v,$pe,$sql,$mysql); }else { if ($pe){ $newdata[$k] = string ::addslashes($v); }else { $newdata[$k] = trim($v); } } } return $newdata; }
发现有启用了过滤函数filter_sql和转义
1 2 3 4 5 6 7 8 9 10 11 function filter_sql (array $data) { foreach($data as $v){ if (is_array($v)){ filter_sql($v); }else { $v = strtolower($v); if (preg_match('/count|create|delete|select|update|use|drop|insert|info|from/' ,$v)){ rewrite::js_back('【' .$v.' 】数据非法'); }
观察开头函数发现有 1 2 $name = urldecode($name);
可以通过两次url编码绕过过滤将注入语句代入getNameData函数进行注入