吃透一文总结,轻松入门代码审计!

admin 2023年12月16日22:28:56评论37 views字数 7544阅读25分8秒阅读模式
免责声明
本公众号提供的工具、教程、学习路线、精品文章均为原创或互联网收集,旨在提高网络安全技术水平为目的,只做技术研究,遵守国家相关法律法规,请勿用于违法用途如果您对文章内容有疑问,可以尝试加入交流群讨论或留言私信,如有侵权请联系小编处理。
2
内容速览

0x00 整体


学习代码审计的目标是能够独立完成对一个CMS的代码安全监测,其通用的思路有

  • 通读全文代码,从功能函数代码开始阅读,例如include文件夹下的common_fun.php,或者有类似关键字的文件
  • 看配置文件,带有config关键字的文件,找到mysql.class.php文件的connect()函数,查看在数据库连接时是否出现漏洞
  • 继续跟读首页文件index.php,了解程序运作时调用了哪些函数和文件以index.php文件作为标线,一层一层去扩展阅读所包含的文件,了解其功能,之后进入其功能文件夹的首页文件,进行扩展阅读

0x01 漏洞


文件操作漏洞

  • 能不用文件名参数就不用,尽量不要让用户可控
  • 平行用户的权限,管理员的权限,操作权限
  • 禁止传入参数类似于这种../<font face="-apple-system, SF UI Text, Arial, PingFang SC, Hiragino Sans GB, Microsoft YaHei, WenQuanYi Micro Hei, sans-serif, SimHei, SimSun">,</font>``检查传入的参数,做出限制,停止程序往下执行

文件包含漏洞


本地文件包含

  • 一般存在于模块加载,模板加载,cache调用
  • 包括函数:include()/include_once()require()/require_once()寻找可控变量 1.php
<?php
	dirfine("ROOT",dirname(__FILE__).'/');
	$mod = $_GET('mod');
	include(ROOT.$mod.'.php');
?>

2.php

<?php phpinfo() ?>
吃透一文总结,轻松入门代码审计!

远程文件包含

  • 前提条件:allow_url_include = on
  • 出现频率不如本地包含

文件包含截断

  • %00截断(php版本小于5.3)
  • 问号截断(问号后面相当于请求的参数,伪截断)
  • 英文(.)反斜杠(/)截断

文件读取(下载)漏洞


搜索关键函数

  • file_get_contents()
  • highlight_file()
  • fopen()
  • read file()
  • fread()
  • fgetss()
  • fgets()
  • parse_ini_file()
  • show_source()
  • file()

文件上传漏洞


搜索关键函数

move_uploaded_file()接着看调用这个函数的代码是否存在为限制上传格式或者可以绕过

未过滤或本地过滤

  • 服务器端未过滤,直接上传PHP格式的文件即可利用

黑名单扩展名过滤

  • 限制不够全面:IIS默认支持解析.asp,.cdx, .asa,.cer
  • 扩展名可绕过
<?php
	function getExt($filename)
	{
		return substr($file,strripos($filename,'.')+1);
	}
	$disallowed_types = array("php","asp","aspx");
	$filenameExt = strtolower(getExt($_FILES['file']['name']));
	if(in_array($filenameExt,$disallowed_types))
	{
		die("disallow type");
	}
	else
	{
		$filename = time().".".$filenameExt;
		move_uploaded_file($_FILES['file']['temp'],'upload/'.$filename);
	}
?>

不被允许的文件格式.php,但是我们可以上传文件名为

1.php(注意后面有一个空格)

文件头content-type验证绕过

  • getimagesize()函数:验证文件头只要为GIF89a,就会返回真
  • 限制$_FILES["file"]["type"]的值 就是人为限制content-type为可控变量

防范

  • 使用in_array()或 利用三等于===对比扩展名
  • 保存上传文件是重命名,规则采用时间戳拼接随机数:md5(time() + rand(1,1000))

文件删除漏洞


搜索关键函数

  • unlink()利用回溯变量的方式
  • 老版本下的session_destroy(),可以删除文件,现已基本被修复 Metinfo的任意文件删除漏洞
<?php
	$action = $_GET['action'];
	$filename = $_GET['filename'];
	if($action = "delete") {
		if(is_array($filenames)) {
			foreach($filenames as $filename) {
				unlink('../../databack'.$filename);
			}
		}
	}
	else {
		if(fileext($filenames) == "sql") {
			$filenamearray = explode(".sql",$filename);
			unlink('../../databack'.$filename);
			unlink('../../databack/sql/metinfo_'.$filenamearray[0].'zip');
		} 
		elae {
			unlink('../../databack/'.$fileon.'/'.$filename);
		}
	}
?>

$action = delete即可删除.sql的文件,如果文件不是sql直接删除提交的文件名

target.com/recovery.php?&amp;action=delete&amp;filename=../../index.php

代码执行漏洞

代码执行函数


搜索关键函数

  • eval()
  • assert()
  • preg_replace()
  • call_user_func()
  • call_user_func_array()
  • array_map()

preg_replace()函数

mixed preg_replace(mixed replacement,mixed limit = -1[,int &$count]])

replacement 会被当做php代码执行

mixed call_user_func( callable parameter [ , mixed $…)

第一个参数为回调函数,第二个参数是回调函数的参数

<?php
	$b = "phpinfo()";
	call_user_func($_GET['a'],$b);
?>
吃透一文总结,轻松入门代码审计!

eval()assert()

当assert()的参数为字符串时 可执行PHP代码

eval("phpinfo();"); = False eval("phpinfo()"); = True assert("phpinfo();"); = True assert("phpinfo()"); = False

动态函数执行


动态函数后门

#!php
<?php
$_GET['a']($_GET['b']);
?>
吃透一文总结,轻松入门代码审计!

命令执行函数


搜索关键函数

  • system()
  • exec()
  • shell_exec()
  • passthru()
  • pcntl_exec()
  • popen()
  • proc_open()

popenproc_open()

#!php
<?php 
popen( 'whoami >> /Users/bingdaojueai/Desktop/1.txt', 'r' ); 
?>

所在路径就会出现一个1.txt 里面的内容为命令执行后的结果

反引号命令执行

  • echo whoami; 直接就可以执行命令
  • 双引号和单引号的区别
$a = 1
echo "$a" = output:1
echo '$a' = output:$a

双引号时,可以直接解析变量,造成代码执行漏洞,过狗绕过

变量覆盖漏洞

函数使用不当


  • int extract(array &amp;$var_array,int $extract_type = EXTR_OVERWRITE,string $prefix = null)
  • void parse_str(string $str,array &amp;$arr)
  • bool import_request_variables(string $type,string $prefix)

变量覆盖


<?php
$a = 1;
foreach(array("_COKKIE","POST","GET") as $_request) {
	foreach($$_request as $_key => $value) {
		echo $_key."<br>";
		$$_key = addslashes($value);
	}
}
echo $a;
?>
吃透一文总结,轻松入门代码审计!

逻辑漏洞


需要思考的问题

  • 程序是否可以重复安装
  • 修改密码是否存在越权修改其他用户密码
  • 找回密码验证码是否可以暴力破解
  • cookie是否可以预测验证存在绕过

等于与存在判断绕过


in_array(): 比较之前会自动转换类型

<?php
	if(in_array($_GET['type_id'],array(1,2,3,4))) {
		$sql = "SELECT ... WHERE type_id = '".$_GET['type_id']."'";
	}
?>
吃透一文总结,轻松入门代码审计!

is_numeric()

当传入参数为hex时 直接通过并返回true 并且MYSQL可以直接使用hex编码代替字符串明文 可以二次注入 并且可能造成XSS漏洞

双等于==和三等于===

  • 双等于会在变量比较时,进行类转换,与in_array()是一样的问题。
  • 三等于是type和value的双重比较,相比之下更加安全

账户体系中的越权问题


  • 水平越权:A用户能够以B用户的身份,进行B用户的全部权限操作。前提A用户和B用户拥有相同的权限
  • 垂直越权:A用户能够以C用户的身份,进行C用户的全部权限操作,前提C用户比A用户拥有更高的权限

exit/return/die

#!php
<?php
if(file_exists('install.lock)){
    header("Location:xxx.com");
    //exit();
}
echo "test";
?>

test依旧会被输出,替换成安装流程,PHP依旧会进行

支付漏洞

  • 客户端修改单价
  • 客户端修改总价和购买数量
  • 服务端未校验严格
  • 重复发包利用时间差
#!php
<?php
if (check_money($price)){
  //Do something
  //花费几秒
  $money = $money 
- $price;
}
?>

可能导致漏洞函数: str_replace()

<?php
	//省略
	$order_sn = str_replace($_GET['subject'],'',$_GET['out_trade_no']);
	$order_sn = trim($order_sn);
	if(!check_money($order_sn,$_GET['total_fee'])) {
		//省略
	}
	function check_money($log_id,$money) {
		$sql = "SELECT order_amount FROM xx WHERE log_id = '".$log_id."'";
		//省略
	}
?>
#!php
<?php
$a = addslashes($_GET['a']);
$b = addslashes($_GET['b']);
echo "$a<br>$b<br>";
$c = str_replace($a,'',$b);
echo trim($c);
?>
吃透一文总结,轻松入门代码审计!

会话认证漏洞


COOKIE验证:没有使用SESSION验证,将信息直接保存在COOKIE中

  1. 找到传入sql语句的参数的传递过程 回溯变量到最原始的函数 看它保存在cookie的算法 是否可逆1. 和MD5比起 sha1更安全 解密sha1的网站更少1. 限制一个用户只能同时在一个IP上登录 审计代码时,查看登录处代码

二次漏洞


类型

  • 不是逻辑问题,是可信问题
  • 业务逻辑复杂度,与二次漏洞触发率成正比
  • 购物车/订单/引用数据/文章编辑/草稿==>SQL注入/XSS
<?php
	$conn = mysql_connect("localhost","root","root");
	mysql_select_db("test",$sonn);
	$a = addslashes($_POST['pwd']);
	if(!empty($a) &amp;&amp; isset($_POST['sub'])) {
		$sql1 = "UPDATE user SET password = '".$a."' WHERE name = 'sixwhale'";
		$sql2 = "SELECT * FROM user WHERE name = 'sixwhale'";
		if(mysql_query($sql1)) {
			echo "update ok<br>";
		}
		else {
			echo mysql_error();
		}
		if($row = mysql_fetch_assoc(mysql_query($sql2))) {
			echo "select ok<br>";
		}
	}
	else {
		echo "no";
	}
	echo "select * from xx where = '".$row['password']."'<br>";
	echo $a;
?>
<html>
	<from action="" method="POST">
		<input type="text" name="pwd">
		<input type="submit" name="sub">
	</from>
</html>
吃透一文总结,轻松入门代码审计!

技巧


钻GPC等转义的空子

  • 不受GPC保护的$_SERVER变量:PHP5以后,$_SERVER取到的header不再受GPC影响,就算开启特殊字符也不会被转义,存在注入 编码问题转换
  1. GBK的宽字节注入:%df ' 单引号自动被转义成(%5c),同时%df%5c连在一起组合成字单引号依然在,成功闭合。【php与mysql交互过程中发生的编码转换问题】1. mb_convert_encoding()
#!php
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> 
<?php
$sql = "WHERE id='".urldecode("-1%df%5c' == ")."'"; 
print_r(mb_convert_encoding($sql,"UTF-8","GBK"));
?>

字符串问题

  • 利用报错,找到敏感信息
  • 字符串截断 %00空字符截断:【PHP版本小于5.3】
#!php
<?php                                 
include($_GET['file'].'.php');      
//1.php?file=2.txt%00
//2.txt里面是 <?php phpinfo()?>
?>

iconv函数字符编码转换截断:【对PHP版本有要求】

#!php
chr(128)—chr(255)可以截断字符
<?php 
$a = '1'.chr(130).'2’; 
echo $a."<br>";
echo iconv("UTF-8", "GBK", $a);   //1
?>

php:// 输入输出流

#!php
<?php
    include($_GET[‘file']);
?>
1.php?file=php://filter/convert.base64-encode(内容被base64编码)/resource=example.txt(远程文件)

php代码解析标签

  1. <script language="php">…</script>
  2. <?…?>:php3.0.4版本后可用
  3. <%…%>:asp标签,需要asp_tags=on,默认是off 正则表达式
  4. 没有使用^ 和 $ 限定匹配开始位置1. 特殊字符未转义 报错注入
<?php
	¥conn= mysql_connect("localhost","root","root");
	mysql_select_db("test",$conn);
	$sql1 = "SELECT 1 FROM (SELECT count(*),concat(USER(),floor(rand(0)*2)) x FROM
			information_schema.TABLES GROUP BY x) a";
	$sql2 = "SELECT * FROM user WHERE id = 1 and 
			(extractvalue(1,concat(0x5c,(SELECT user()))))";
	$sql3 = "SELECT * FROM user WHERE id = 1 and 
			(updatexml(1.concat(0x5c24,(SELECT user()),0x5e24),1))";
	$sql4 = "SELECT * FROM user WHERE id = 1 and 
			GeometryCollection((SELECT * FROM(SELECT * FROM(SELECT user())a)b))";
	$sql5 = "SELECT * FROM user WHERE id = 1 and 
			polygon((SELECT * FROM(SELECT * FROM(SELECT user())a)b))";
	$sql6 = "SELECT * FROM user WHERE id = 1 and 
			multipoint((SELECT * FROM(SELECT * FROM(SELECT user())a)b))";
	$sql7 = "SELECT * FROM user WHERE id = 1 and 
			multilinestring((SELECT * FROM(SELECT * FROM(SELECT user())a)b))";
	$sql8 = "SELECT * FROM user WHERE id = 1 and 
			multiolygon((SELECT * FROM(SELECT * FROM(SELECT user())a)b))";
	$sql9 = "SELECT * FROM user WHERE id = 1 and 
			linestring((SELECT * FROM(SELECT * FROM(SELECT user())a)b))";
	$sql10 = "SELECT * FROM user WHERE id = 1 and 
			exp(~(SELECT * FROM(SELECT user())a))";
	$res = mysql_query($sql10);
	if(!res){
		echo mysql_error();
	}
?>
吃透一文总结,轻松入门代码审计!

windows findfirstfile 利用:若要搜索12345.txt文件,可使用1<<来代替或者12<<,不可以单独使用一个"<"">",因为单独一个只是代表了一个字符,两个代表多个字符

参考资料

[0]参考文章:https://blog.csdn.net/m0_60640202/article/details/121713032

原文始发于微信公众号(网络安全自修室):吃透一文总结,轻松入门代码审计!

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月16日22:28:56
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   吃透一文总结,轻松入门代码审计!https://cn-sec.com/archives/2307663.html

发表评论

匿名网友 填写信息