Friday, June 16, 2017

How I rooted Proteus: 1

Ivanza had prepared a treat for us and published it to Here are the steps I used to root it.

First I used arp-scan to determine what IP address the target got:

Then I simply ran nmap (with the default settings) against the target:
Only ports 22 and 80 open. 22 was confirmed to be SSH and 80 was a web server. Here's the default web-page:
I ran nikto and dirb against the target, but they didn't really reveal anything interesting, so I then focused on the file upload for, right in the front page. The service checked the contents of the file and a simple file extension manipulation didn't do the trick. Luckily the front page already told us what is accepted: Mime-types application/x-executable and application/-sharedlib. I made a simple Hello World -application for upload:

When the file was uploaded, the service shows a simple analysis of the file (results of the commands "strings" and "objdump":

Both of the commands are command line utilities, which take the file name as the parameter. Perhaps the file name can be used for os command injection. And sure enough, here's what I sent:
And here's an excerpt of the response:
When I then tried to execute something more complex, the command injection failed. After some probing and prodding, I came to the conclusion that slashes (/) and periods (.) break the command injection. So what could be done without either?

To have unlimited command execution on the target box, I used the following information:

  1. you can provide IP-address for applications (such as curl and wget) in the decimal notation, so you don't need periods and
  2. pythons SimpleHTTPServer module serves the contents of index.html by default
I first calculated the decimal notation of my IP:
 Then I modified my payload as follows:
My index.html only contained a simple "cat /etc/passwd" and here are the results:

Great, now I was able to execute a remote shell. I chose the python reverse shell:

 and set up an nc listener on my own end:

And I had a much more comfortable shell. I then proceeded to fire up a pseudo tty with python (python -c "import pty;pty.spawn('/bin/bash')") and started examining the server.

I eventually found a file called admin_login_request.js
The file comments say that the file is used to impersonate admin, but I still thought that if that password isn't supposed to be used, it will not allow me to access anything. But:
And the user even had sudo rights:
But because of the comment, I thought that there probably is yet another way to admin. And sure enough, there was a root suid binary, which caught my attention.

When I was testing out the file (I downloaded it to my own computer first), I determined that the application takes a single command line argument and writes that to the end of a hard coded log file. If the parameter is too long though, it will overflow to the file name and eventually overwrite it. By passing a value of exactly 456 bytes, you overwrite the filename with NULL (0x00), effectively making the file name an empty string:
So now I can write to any file I choose with root permissions, as long as what I was writing was over 456 characters and didn't contain any spaces or newlines (nor nulls). Since the text was always appended to the target file I thought that I could add a new line to the /etc/passwd file and went with the idea.

Here's the python script I used to exploit the vulnerability:
And here I run the actual exploit
Time to confirm the results:

Btw, the password has was created with the command "openssl passwd -1 -salt xyz  yourpass".
After obtaining root privileges on the target I found a file flag.png from the /root folder:
So this is it. Thanks for Ivanza for creating the challenge and the good guys at vulnhub for hosting it.

Thursday, June 15, 2017

Pitfalls to avoid in Robot Framework

I recently was working on a vulnerability assessment project, where the developers had used robot framework in the backend for their product. I hadn't had the pleasure to work with robot framework before, so I was eager to start. Robot Framework is a great project, which can be used to automate most of your testing needs.

What I found was a bit worrying for me and lead me to the conclusion that robot framework hasn't been designed to be used with external input.

I will now show you a simplified example of the problem I discovered. Although in my examples I pass values to the scripts variables from the command line, they work in the same fashion if robot framework is called programmatically.

Consider the following simple example:

The code should simply test if the variable "var" is empty and if it's not, the value is then printed to the console. You can run it as follows:
If the variable would've been empty, we would've been missing the "We got: foobar" output.

Simple enough, eh?

The example above contains the issue I discovered in the assessment. Let's say that the variable "var" comes from outside, originally from untrusted source. You might ask: so what? it's just a variable. Technically you would be correct, but what if I told you, that to parse the condition and determine if it's true or not, it will be passed to the pythons eval function? And what if I told you that since Robot Framework 2.8 the sys and os modules are imported by default when evaluating the condition? You might be shocked, but if you aren't let me explain the issue with a few more details.

While performing the assessment, I had a test case, where I only entered a double quote as the value and got the following error:
Evaluating expression '""" != ""' failed: SyntaxError: EOF while scanning triple-quoted string literal (, line 1)

My initial thought was that I managed to break some python script, because the SyntaxError is what python raises. But when you enter the whole exception to google, you see that it's actually coming from the Robot Framework (which calls python in the background).

So, what is entered to the variable called var, is passed to the pythons eval with the surrounding quotes. With the sys and os modules imported by default, we can trivially execute operating system commands, but the injections are blind. For the attacker the default imports are really helpful, after all you can't import new modules from the eval -function. Let's demonstrate it again with another example:
Here we see, that if the python syntax is seemingly correct, we get no errors. You might get the command output with for example os.system call, but that would only print the result to stdout and in the assessment, I never saw any output from my commands, no matter how I called them. When I tried to execute something along the lines of "sleep 20" I was able to confirm that os commands are actually executed. When I have access to the operating system, it becomes much easier:
First, I check that there is no file called bar and that it appears with the echoed contents after calling robot framework.

So, how could I exploit this? If the server is allowed to create TCP connections out to you, then the exploitation becomes trivial. Simply share a backdoor with http, then make the server download it with wget and finally execute it.

I used msfvenom to generate the payload, but anything goes here. Here's what I executed (yea, I'm using localhost for the demo here):
And here's what I see on my SimpleHTTPServer and nc shells:
I think the issue should be clear by now. Naturally I could've just told you to go RTFM.

According to the robot fw documentation:
Many keywords, such as Evaluate, Run Keyword If and Should Be True, accept an expression that is evaluated in Python. These expressions are evaluated using Python's eval function so that all Python built-ins like len() and int() are available. Evaluate allows configuring the execution namespace with custom modules, and other keywords have os and sys modules available automatically.

Currently there's also another way for using variables:
Starting from Robot Framework 2.9, variables themselves are automatically available in the evaluation namespace. They can be accessed using special variable syntax without the curly braces like $variable. These variables should never be quoted, and in fact they are not even replaced inside strings.
Using the $variable syntax slows down expression evaluation a little. This should not typically matter, but should be taken into account if complex expressions are evaluated often and there are strict time constrains.

In my personal experiments, when variables are used with the latter syntax ($var), the injections don't work anymore. So rather than trying to blacklist all the possible ways to inject python code, you might want to use the new syntax and take the the hit from slower evaluation. If I'm mistaken, please leave a comment.

In the examples I've used Robot Framework 3.0.2

From the builtins page you can see that at least the following keywords use evaluation:
  • Continue For Loop If
  • Evaluate
  • Exit For Loop If
  • Pass Execution If
  • Return From Keyword If
  • Run Keyword And Return If
  • Run Keyword If
  • Set Variable If
  • Should Be True
  • Should Not Be True
And no, I have no idea if it's used elsewhere, so don't take the above as a definitive list.

TL;DR think again before passing external data to the robot framework variables.