代码审计练习之sql注入


前言

暑假简单的学了一下代码审计,整几个简单的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
/**
* [bluecms]��Ȩ���� ��׼���磬��������Ȩ��
* This is not a freeware, use is subject to license terms
*
* $Id��ad_js.php
* $author��lucks
*/
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函数进行注入


代码审计练习之sql注入
http://example.com/2023/09/13/代码审计练习之sql注入/
作者
John Doe
发布于
2023年9月13日
许可协议