[::ACSC Quals 2021::] — Breaking Logics

Yesterday, I have played ACSC 2021 and there an interesting challenge from Orange Tsai and I want to write down something about this challenge. The challenges from ACSC so good and it will be up for 1–2 days. If you curious about them, go head https://score.acsc.asia/

This challenge is easy to approach but I dont know while Orange didn’t give us the Dockerfile like allmost of web challenge

  1. I try Tomcat 8 + Java 8 for local but it cant be up because Wro compatible Java 9
  2. then I try Tomcat 7 + Java 9, but this time the payload didn’t work as usual because the different logic between servletContext.getRealPath() of Tomcat 7 and Tomcat 8
  3. Finally I try Tomcat 8 + Java 9 and it matchs the remote environment, this make me watse a lot of time for setting environment :(

Enviroment setup

unzip the WAR and import it to IDEA

Orange gave us a war file of this challenge, and we need to spin up local enviroment to debug this challenge

At first I setup with wrong java version so it didn’t work as usual. Later when a analysis via IDEA I noticed that the actual java version is 9

Notice the Java bytecode version

This is the enviroment I used for local Tomcat 8 with Java 9

Tomcat 8 and Java 9

Next, we need to deploy the WAR which Orange gave us and make it’s context to ROOT

ROOT context of tomcat

Next I added to “JAVA_OPTS” in catalina.sh to enable remote debugging

And config remote debug in our IDEA also

config remote debug on IDEA

Now everything work perfectly for us to find Orange’s 0day

Analysis

Our job is need to find a way to read the “flag.jsp”

When we access the challenge, there’s an additional request to get the resource at “/wro/all.js” which actually triggers WroFilter

web.xml

And the challenge only aim to this Filter, so I need to know how it works. First I added some breakpoint at WroFilter.doFilter()

WroFilter.doFilter()

After hit the breakpoint with our request to “/wro/all.js”

There are 2 branches, handledWithRequestHandler() and processRequest(), continue and we reach handledWithRequestHandler()

At this point we could see that Wro handle the requsest with 4 RequestHandler and if we want to step into each RequestHandler we need to pass the accept() method of each RequestHandler.

And ResourceProxyRequestHandler looks promising for us !

In order to let ResourceProxyRequestHandler accept our request, our request must valid the isProxyRequest() method. So our request to step into ResourceProxyRequestHandler is something like this

After we step into ResourceProxyRequestHandler there’s an additional check if our request is authorized or not

But there’s a big hole when checking the uri

Wro remove the queryString before it check that the resource is in the authorizedResources or not

So we can put any string after “?” to pass this check

When we pass the check Wro try to get our request resource and copy it’s content to our HTTP Response

Now we need to focus the locate() function which will locate our request resource and send it to us. I replace “ANY_STRING” with some the traversal payload “/../../flag.jsp” and then realised that we actually traversal to ROOT context

Continue and step into locateStream() function, Wro finnaly fetch flag.jsp for us

and this is our Flag

But there’s a little bit noise when the challenge deploy on Tomcat 7, when re-calculating the realPath, it miss one slash at the end, so this will make locateStream() throw Exception because our realPath is invalid

After pass the Exception, Wro will dispatch the uri with RequestDispatcher, now our payload “/asset/debug.js?dummy/../../flag.jsp” will be dispatched with the RequestDispatcher, it means that “?dummy/../../flag.jsp” parsed as query string and will be ignore, and we finnaly get the content of “/asset/debug.js”

Conclusion

I don’t know why Orange didnt give us full Dockerfile about this challenge and this make me waste a lot of time to figure it out

One more thing, if you request to “/wro/wroAPI/reloadModel” Wro will reload the model and the authorizedResources will be empty in the check step above, and it always return false, so you can’t get your flag at the time you test the server if anyone trying to request to “/wro/wroAPI/reloadModel” repeatedly.

Anyway, ACSC 2021 is a good CTF contest. I enjoy the time, all challenges. And thanks the organizer for making such a good CTF !

Nub-boi