弱类型语言——PHP

PHP脚本以<?php开始,以? >结束

变量以$符号开始,后跟变量名,必须以字母或者下划线开始,并区分大小写

array用于创建数组,count()函数获取数组长度,foreach()关联数组的创建及遍历

局部作用域和全局作用域

global关键字用于函数内访问全局变量 static作用域:使某个局部变量在函数结束后不被删除

两种比较符号:

==弱等于:在比较前把两种字符串类型转成相同的在进行比较,即只比较值。

===强等于:先比较类型,不同直接返回不相等,既比较值也比较类型。

示例simple.php

**$a == 0**:传递a=a

这里的 $a 是字符串 "a"。在 PHP 中,弱类型比较时,非数字的字符串与数字进行比较时,字符串会被转换为数字。

字符串 "a" 会被 PHP 转换为 0,因为 PHP 会尝试从字符串的开头提取数字,而 "a" 并没有任何可提取的数字部分,所以转换的结果是 0

因此,"a" == 0

错误控制运算符@

当在某个表达式前加上 @,如果该表达式出错,PHP 不会显示错误信息,而是返回 falsenull

index.php

index.php是一个用PHP语言开发的网站的首页,index是普遍意义上的“首页”,也就是你输入一个域名后会打开一个页面,基本上就是index.xxxx(基本上首页都不会把index.xxxx显示在url里,但也不绝对)

index.php是一种常见的网页文件,它通常用于网站的首页或默认页面。在使用PHP编程语言的网站中,index.php文件是网站的入口点,它负责处理和呈现网页内容。

当访问一个使用PHP编写的网站时,服务器会首先查找并执行index.php文件。该文件可以包含PHP代码、HTML标记和其他相关内容,用于生成并呈现动态的网页内容。

PHP2(PHPS)

phps文件就是php的源代码文件,通常用于提供给用户直接通过Web浏览器查看php代码的内容。

因为用户无法直接通过Web浏览器“看到”php文件的内容,所以需要用phps文件代替。用户访问phps文件就能看到对应的php文件的源码。

PHP序列化和反序列化

序列化字符串的标准格式: O:<类名的长度>:"<类名>":<成员属性的个数>:{S:<成员属性名的长度>:"<成员属 性名>";......}

序列化serialize():是将变量或对象转换成字符串的过程( 序列化只作用于对象,不序列化方法),用于存储或传递 PHP 的值的过程中,同时不丢失其类型和结构。

反序列化unserialize():将字符串转换成变量对象的过程,是一种将存储在字符串中的PHP对象转换为PHP变量的过程。这可以通过使用unserialize()函数来实现。

PHP魔术方法

__sleep()

serialize() 函数会检查类中是否存在一个魔术方法 __sleep()。如果存在,该方法会先被调用,然后才执行序列化操作。此功能可以用于清理对象,并返回一个包含对象中所有应被序列化的变量名称的数组。如果该方法未返回任何内容,则 NULL 被序列化,并产生一个 E_NOTICE 级别的错误。

对象被序列化之前触发,返回需要被序列化存储的成员属性,删除不必要的属性。

__wakeup()

unserialize() 会检查是否存在一个 __wakeup() 方法。如果存在,则会先调用 __wakeup 方法,预先准备对象需要的资源。

漏洞:当序列化字符串表示对象属性个数的数字值大于真实类中属性的个数时就会跳过__wakeup的执行。

预先准备对象资源,返回void,常用于反序列化操作中重新建立数据库连接或执行其他初始化操作。

用php脚本实现哈希值爆破

```

关于substr函数:substr(string $string, int $start, ?int $length = null): string

**$string**:要操作的原始字符串,以上即为 MD5 哈希值字符串。 **$start**:提取的起始位置。-6 表示从字符串的倒数第 6 个字符开始。 **$length**:要提取的字符数,这里是 6,表示提取 6 个字符。

文件上传漏洞

常见的形式(对应于upload-labs靶场):前端验证、文件类型验证、黑名单验证(.htaccess解析绕过、大小写绕过、空格绕过、

上传了一个可执行的文件到服务器并执行(某些网站中没有严格限制用户上传文件的后缀名及类型,导致可以上传任意PHP文件,并能够在远程服务器上执行任意PHP脚本)

基础知识:

Webshell

以php等网页文件形式存在的一种命令执行环境(网页后门),混在web目录下正常的网页文件,隐蔽性高,可以轻松穿越防火墙,且访问 WebShell 时不会留下系统日志

一句话木马:

利用文件上传漏洞,通过攻击者传递的命令获得服务器的控制权。

<?php @eval($_POST['attack']);?> @表示后面即使执行错误也不报错;eval()函数表示括号内的语句字符串都当作代码执行。$_POST[‘attack’]表示从页面中获得attack这个参数值。

例:使用eval()函数执行PHP代码 <?php eval($_POST['cmd']); ?> 通过POST请求传递PHP代码,eval() 会将这些代码作为PHP代码执行。

图片木马

  1. 在路径下准备好一句话木马.php和一张图片.png(或者 .jpg )

  2. 输入系统指令: copy 一张图片.png/b+一句话木马.php/a 生成图片名称.png

  3. 这样图片木马就合成好了

    doc、pdf、excel文件头木马写入

  4. 准备word文档.doc + .exe木马文件

  5. 输入系统指令:copy CSDN.doc/b + 1.exe/a phpinfo.doc

常见的绕过方式

利用科学计数法绕过长度限制

1e9的格式表示大数值,PHP自动识别为浮点数,假设限制5位数字,此时长度仅为3个字符,从而绕过长度限制

js前端校验绕过

删除js校验代码(f12调试器禁用js)、更改js校验中的白名单、先改文件后缀名通过校验后抓包更改后缀上传至服务器

分布式配置文件.htaccess

.htaccess文件解析漏洞可以利用其将我们所的文件都解析成php或者是特定的文件解析为php,与下面,user.ini大同小异

.user.ini绕过

关于.ini配置文件

.ini文件主要用于服务器端的PHP配置,定义PHP的设置参数,

.user.ini实际上就是一个可以由用户”自定义”的php.ini.

.user.ini为特定目录设置PHP配置,允许在不访问PHP主配置(php.ini)的情况下,对特定目录的PHP行为进行局部配置,而且可以由用户在应用运行时动态配置(即auto_prepend_file 可由 .user.ini 文件动态设置且定期加载,允许攻击者不重启服务器即可立即执行恶意代码。)

PHP配置项的利用

auto_prepend_file 允许在执行某个 PHP 文件之前,自动加载并运行指定的文件。即指定的文件中的内容会首先执行,可以被用来控制 PHP 代码执行的顺序

通过上传 .user.ini 文件来设置 auto_prepend_file,并将其指向一个伪装为图片的恶意文件。绕过文件类型检测,该文件伪装成图片文件,在文件头部添加 GIF89a

绕过方式:

首先上传包含上述内容的.user.ini内容文件(auto_prepend_file = “a.jpg),然后上传包含有一句话木马的a.jpg

过滤绕过(windows)

大小写转换、点(.)、空格、特殊字符::$DATA

点空格点(. .)绕过

原理:由源码可知,对文件名末尾的点,大小写转换,去除了::$DATA,首位去空了,但是这些操作只会执行一次,所以我们可以抓包,加上后缀. .,这样程序在经过处理之后,我们的文件名就只剩一个.,可以绕过黑名单检测,最后在服务器中自动去除.,我们就可以正常访问img

双写绕过

双写绕过就是双写文件后缀名来进行绕过,如:test.php 双写后为 test.pphphp。

通常情况下双写绕过用于绕过源代码中的

img

原理:源代码中有黑名单和str_ireplace()函数的联合使用,将黑名单中的敏感后缀名全部都进行了删除。这就会使我们上传的 test.php 上传后变成 test. ,这就会使Apache无法解析,以至于不会执行文件中的php代码。 而str_ireplace() 函数的缺陷是只会进行一次删除操作,我们这时就可以使用双写进行绕过。test.pphphp 被删除一次php后依然还剩 test.php,这样就可以成功执行php代码。

文件名截断

GET方式%00绕过(白名单过滤)

关键源码: $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

逐行分析:

1
2
3
4
5
6
7
if(isset($_POST['submit'])){
$ext_arr = array('jpg','png','gif'); //白名单上传(只允许上传给定的后缀名)
$file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1); //读取上传文件的后缀
if(in_array($file_ext,$ext_arr)){
$temp_file = $_FILES['upload_file']['tmp_name']; //获取上传文件名(无后缀)
$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
// 将save_path的值提取(默认为../upload/)中间拼接了一个随机数和年月日时分秒再上传文件的后缀;而save_path是可控的变量,故可进行改写路径并加%00

条件:上传的路径可控、PHP版本低于5.3.4、magic_quotes_gpc为OFF状态

解释00截断:
当 PHP 在处理文件名或路径时,遇到 URL 编码的 %00,它会被解释为一个空字节(ASCII 值为 0)。在php5.3以前,PHP 会将这个空字节转换为 \000 的形式。而在php5.3以前,文件名出现\0000,会导致文件名被截断,只保留%00之前的部分。

这是因为php语言的底层是c语言,而\0在c语言中是字符串的结束符,所以导致00截断的发生

POST方式%00绕过

形式只是将上面代码中的GET换为POST($_POST['save_path']

但以POST形式传参时,直接使用%00无法截断,因为POST的数据不会被直接解码,需要手动使用burp中的URL-decode解码

图片🐎(文件头绕过)

源码getReailFileType($filename)使用了文件头检测,故不能只修改后缀要修改文件头使文件具有jpg特征

  1. 伪造文件格式——绕过文件头验证

GIF89a伪造文件格式的实现:

(文件头标识符:文件类型在文件开头都有独特的二进制标识符,用来标识文件格式。如:GIF文件通常以 GIF89aGIF87a 开头)

在恶意代码前插入伪造的文件头:为了使一个PHP脚本伪装成GIF文件,在PHP代码前加上 GIF89a,即伪造一个GIF文件头。服务器很可能不会进一步检查文件内容,认为这是一个合法的图片,从而通过了上传验证。

  1. 合成图片马,利用指令copy 一张图片.png/b+一句话木马.php/a 生成图片名称.png

函数绕过

$info = getimagesize($filename);

getimagesize函数会对目标的十六进制的前几个字符串进行读取。比如GIF的文件头问GIF89a,png的文件头为塒NG。所以这关和2.9一样,我们只需要用notepad打开图片马,在前面加上GIF89A,保存为webshell.gif

$image_type = exif_imagetype($filename);

exif_imagetype读取一个图像的第一个字节并检查其签名

二次渲染

$im = imagecreatefromjpeg($target_path); //使用上传的图片生成新的图片

二次渲染说白了就是上传的图片,会重新创建画布,然后把里面的东西重新渲染一边,二次渲染会将图片马里面的php代码删除,使其失效

思路:找到渲染后的图片里面没有发生变化的Hex地方,添加一句话,通过文件包含漏洞执行一句话

条件竞争

代码审计:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$is_upload = false;
$msg = null; //判断文件上传操作

if(isset($_POST['submit'])){ //判断是否接收到了这个文件
$ext_arr = array('jpg','png','gif'); //声明一个数组里面有3条数据,为:'jpg','png','gif'
$file_name = $_FILES['upload_file']['name']; //获取图片的名字
$temp_file = $_FILES['upload_file']['tmp_name']; //获取图片的临时存储路径
$file_ext = substr($file_name,strrpos($file_name,".")+1); //通过文件名截取图片后缀
$upload_file = UPLOAD_PATH . '/' . $file_name; //构造图片的上传路径,这里暂时重构图片后缀

if(move_uploaded_file($temp_file, $upload_file)){ //对文件进行转存
if(in_array($file_ext,$ext_arr)){ //对比截取到的后缀名和数组里的后缀名
$img_path = UPLOAD_PATH . '/'. rand(10, 99).date("YmdHis").".".$file_ext; //通过rand随机数和date时间戳来生成文件名
rename($upload_file, $img_path); //重命名文件
$is_upload = true;
}else{
$msg = "只允许上传.jpg|.png|.gif类型文件!";
unlink($upload_file); //删除文件
}
}else{
$msg = '上传出错!';
}
}

原理:服务器先通过move_uploaded_file把文件保存了,然后再去判断后缀名是否合法,合法就重命名,如果不合法再删除。重点在于,在多线程情况下,就有可能出现还没处理完,我们就访问了原文件,这样会导致被绕过。(服务器处理请求时有一定的时间间隔)上传文件后服务器还未检验将其删除,文件存在时进行访问。

处理思路:bp抓包–>爆破(线程调为20,字典number选择1000)–> Start attack

文件包含

php://filter

php://filter作为中间流来处理其他流

resource=<要过滤的数据流> read=<读链的筛选列表> write=<写链的筛选列表>

String Filters(字符串过滤器)

Conversion Filters(转换过滤器)

利用姿势:php://filter/convert.base64-encode/resource=flag.php