Write-up CTFZone Quals 2019: Chicken

Despite the postponement of the OFFZONE 2020 conference , there will be a CTFZone final ! This year it will be held for the first time online and will be actively broadcast on social networks.

We will announce the details later, but for now, we suggest exploring the webtask startup from the qualifying stage. Analysis of the solution was sent to us by Devand MacLean from Canada. Especially for Habr, we have translated the text of the local startup and we invite you to find out which chain of vulnerabilities the participants encountered and what does the chicken have to do with it.



general information


Author of task: Pavel Sorokin
Points: 470
Number of teams that solved task: 2

Useful links:




, - :


  • (/Home/Index);
  • (/Home/Hens);
  • (/Home/Contact);
  • (/Auth/Login).

,   , :


  • ;
  •  API ,   .


The / Home / Hens page contains links to download chicken “passports”.


After decoding Base64, we find the parameter filename: 1.txt. Using CyberChef, we encode / etc / passwd in Base64 and follow the link:

http://web-chicken.ctfz.one/File/Download?filename=L2V0Yy9wYXNzd2Q=

The contents of the / etc / passwd file on the server are opened in the browser , which means we get the right to read arbitrary files in the system.

When you try to find other files in the system, you may notice an error that appears every time you request a nonexistent file (or a file that cannot be read with the user rights of the application):


Having seen that the error refers to the ASPNETCORE_ENVIRONMENT environment variable , we begin to study ASP.NET Core web application layouts. And we see the following:


To convert the necessary paths and file names and extract them to Base64, we write a small Python script ( fetch.py ):


Using this knowledge about the structure of the MVC web application created in ASP.NET Core, using fetch.py ​​we get the source code for the following files:


  • ../Views/Shared/_Layout.cshtml
  • ../Views/Home/Index.cshtml
  • ../Views/Home/Contact.cshtml
  • ../Views/Home/Hens.cshtml
  • ../Views/Auth/Login.cshtml

Having done this and having studied the source code of each page, we find an interesting detail in Hens.cshtml


Line 1 contains the inclusion statement @ using StackHenneryMVCAppProject, which in ASP.NET means a link to a DLL file.

We open ../StackHenneryMVCAppProject.dll - but not through the Python utility in Kali Linux, but in the browser on Windows, because we are going to decompile this file in Windows. Through the use of such the URL:

http://web-chicken.ctfz.one/File/Download?filename=Li4vU3RhY2tIZW5uZXJ5TVZDQXBwUHJvamVjdC5kbGw=

Opening DLL-file in dnSpy (.NET decompiler), immediately see that in the method Initialize () class Config. Configuration , which runs when the web application starts, reads the contents of the chicken_domains_internal.txt file . Using a Python script, we extract the contents of this file:

web-chicken-flag
web-chicken-auth

The Initialize () method connects to  web-chicken-flag via port 4321 and receives several parameters: secret_token , RSA_key and  flag .

Unfortunately, it is not possible to initiate a connection to web-chicken-flag. But, when we set the local DNS hosts record to translate web-chicken-auth to the server’s external IP address ( 34.89.232.240 ), we manage to open http: // web-chicken-auth / with the Swagger UI interface that is used to describe and execute commands through the REST API.


While digging into the decompiled DLL file, you can notice that the AuthController controller had not only the Login () method, but also the Change_Password_Test () method :


When we go to  / Auth / Change_Password_Test we see this form:


When studying the Change_Password_Test () method, we understand that it processes four parameters of type String through an HTTP POST request:


  • username;
  • new_password;
  • old_password;
  • base_url.


A little lower in the Change_Password_Test () method we see: without receiving a value for base_url , the method takes the value conf.auth_server , that is web-chicken-auth , and then adds / auth / login to the end of the base_url value , assigning the resulting string to the requestUri variable :


In the next important part of the code, the string values ​​of the username and  old_password parameters from the HTTP POST request, and with them the  secret_token from the configuration, are inserted into the JSON string. Then, a JSON StringContent object is created from this data , which is sent as HTTP POST data to the requestURI variable created in the previous step.


Now the server waits for the HTTP POST request to  requestUri to return the JSON object with the code parameter equal to 0. If this does not happen, we get to the code branch that starts from line 6 and returns the view / Views / Auth / Login with the error Invalid login / password .

If the HTTP POST request still returns a JSON object with the code parameter equal to 0, we go to the code branch from line 11. Then the requestUri2 variable gets the value http: // web-chicken-auth , another JSON StringContent object is createdwith the parameters from the original HTTP POST request, as in the previous part, and this data is sent to the requestUri2 address  in the HTTP PUT request. Finally, the Password changed message is returned to the client .


In a nutshell: the whole process begins with an HTTP POST request to  / Auth / Change_Password_Test , which expects a certain number of parameters. It takes these values ​​and creates a JSON object to send it to  <base_url> / auth / login via HTTP POST. If in response it receives JSON with the code parameter equal to 0, it creates another JSON object and sends an HTTP PUT request to  http: // web-chicken-auth / auth / change_password .

When learning the REST API in Swagger UI, we see that in the decompiled DLL file there were not only the routes / auth / login and  / auth / change_password , but also the route for / auth / password_recovery .


This API route expected a JSON object with two parameters: email and  secret_token . If he received it, then he returned a JSON object with a code property of 0 and message and  token strings .


It turns out that if we somehow force the Check_Password_Test () method to immediately request the route / auth / password_recovery instead of / auth / login , then to enter the password change branch it will be enough to enter a valid email address.

We again study the part of the code in which the Check_Password_Test () method assigns the requestUri value in the original HTTP POST request, and we understand that we need to send the value http: // web-chicken-auth / auth / password_recovery # as the base_url parameter, and requestUri in the final the result will be http: // web-chicken-auth / auth / password_recovery # / auth / login .

The # character in the HTTP URL indicates that part of the request address has ended. Therefore, the server that receives the request will simply ignore the / auth / login part .


This, of course, is good, but we just have to figure out how the REST endpoint, where we now redirect the request, will receive the email value . Without this value, the request will fail, and we will not go any further.

We carefully look at how the Check_Password_Test () method creates JSON data, and we see that the input data is not cleared at all, which means that you can easily implement your parameters. It is enough to send the following parameters in the HTTP POST request:

username = admin
old_password = pwned "," email ":" admin@chicken.ctf.zone "," lol ":"
new_password = p0tat0
base_url = http: // web-chicken-auth / auth / password_recovery #

The resulting JSON object is sent to  / auth / password_recovery :


With this JSON data, we met the requirements that / auth / password_recovery should return a response, after which the server will enter the password change branch (the JSON object contains the email property ). Now we no longer need the value of old_password . In this code branch, we provided all the values ​​necessary to change the password (the username property is admin and the password property is p0tat0 ). The request is completed, and we get Password changed in response.


It remains only to enter a new username and password on the login page!


And here is our flag:


All Articles