Python security-from SSRF to command execution massacre

Source: Internet
Author: User
Tags imagemagick redis version serialization ssh port cve

The first two days encountered a problem, the origin is in a packet to see url= this keyword, at that time, at the moment to think there will be ssrf loopholes.

In the past, there were many cases of SSRF hitting the intranet and executing commands, such as the case of roaming the intranet through the ssrf+s2-016 loophole, which is very classic. But when I got this goal, I just wanted to make sure that he was not a ssrf loophole, and didn't expect to find a lot of interesting things behind it. Not much (some of the back to fill), we do not look at it.

0x01 Judge Ssrf Vulnerability

The goal example.com , depending on the style of the csrf_token, I guess it's developed for flask (or maybe a framework I'm not quite familiar with and FLASKWTF similar code):

Open the agent to browse through the entire site function, the function point is not many, compared to a small audience of a sharing site. Accidentally in the packet url= to see, looked at the discovery is a localized external image such a feature. This feature is prone to two types of vulnerabilities:

    1. SSRF Vulnerability
    2. XSS Vulnerability

SSRF the vulnerability is not much to say, when pulling external resources without checking the URL, resulting in the ability to send requests to the Intranet, XSS vulnerability is easy to ignore, pull to the target after the storage without filtering special characters, it can lead to XSS vulnerability.

Simple fuzz, Access http://127.0.0.1:80/ , http://127.0.0.1:80/404404404not_foundhttp://127.0.0.1:12321/

The error and two 500 are returned in turn, what do the three results represent?

Because there are more python development than usual, this 500 of the situation also see more, usually because the code does not catch the exception resulting in the return of 500. Feel the second possibility is that HTTP request 404 causes an exception to be thrown, and the third may be that the TCP connection is denied (port 12321 is not open) and causes an exception to be thrown.

Although I haven't yet cleared up the code logic for the goal, I'm sure there are ssrf holes in it.

0x02 Chicken and Redis service?

After a simple test, I found that when the target site downloaded the external resources, it checked to see if the resource type was a picture, not the picture, and returned the error. This is very embarrassing, this is a no echo of the SSRF vulnerability.

At this time, I suddenly thought, since it is the judge of the picture, it will be used ImageMagick components to judge? I then saved the Imagetragick POC to a poc.gif in the extranet and then made it accessible:

Directly returned the content, no error, no 500, but the command did not execute successfully.

At that time did not think clearly how the goal is how to judge the picture, and later to get the shell to see the source code to know: The goal is to determine the return package Content-type, if not the image directly return error, I think too complex.

ImageMagick This road is dead, I have not studied this logic. Because I do not know the target network IP segment, so prepare to detect the 127.0.0.1 port, I listed some common ports, with Burp ran a bit:

When I saw that 6379 was 200, I was really excited, as we all know, it's a pleasure to encounter redis in penetration.

But I quickly found out that I couldn't control Redis's commands with get requests.

Science, Redis protocol is a simple text flow, such as I can send to 6379 port the following TCP stream:

SET x 1SET y 2

Each row represents a command, and the above packet executes two set commands. But now it's embarrassing that the packets for the normal GET request are as follows:

HTTP/1.1Hostexample.comAccept*/*accept-languageenuser-agent  mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; trident/5.0)Connectionclose           

I can't control the starting part of any line, and I can't customize the Redis command, very chicken ...

0x03 cve-2016-5699 the magic of Decay

Do you really have a chicken?

Last year, Python's Urllib library had a header-injected vulnerability, cve-2016-5699 (http://blog.neargle.com/SecNewsBak/drops/Python%20urllib%20HTTP%E5% a4%b4%e6%b3%a8%e5%85%a5%e6%bc%8f%e6%b4%9e.html)

Because there are similar ideas in the CTF, so I think of the basic first time. I opened a port on the external Web server with NC, and in the landing page, I found that the http://[vps-ip]%0d%0aX-injected:%20header:12345/foo injection was successful:

A little excited, because before just heard of this loophole, no real case of relying on, this time really met. If we can inject an HTTP header, we can control a row of packets destined for redis so that we can execute any redis command.

A little afraid of affecting the target station, I first set up a similar environment in the local.

There are several ways to attack Redis, the core of which is to write files. In a local test, several giant pits were found:

    1. CONFIG SET dir /tmp, the pass slash / must be encoded two times ( %252f ), otherwise urllib2 will throw URLError: <urlopen error no host given> an exception.
    2. The URL is too long to cause UnicodeError: label empty or too long an exception to be thrown, so I need to pass in CONFIG SET and SAVE wait for several commands.

Finally, I sent http://127.0.0.1%0d%0aCONFIG%20SET%20dir%20%252ftmp%0d%0a:6379/foo ,, http://127.0.0.1%0d%0aCONFIG%20SET%20dbfilename%20evil%0d%0a:6379/foo , and http://127.0.0.1%0d%0aSET%20foo%20bar%0d%0aSAVE%0d%0a:6379/foo finally succeeded in writing the /tmp/evil file locally.

But the target environment is a bit of egg ache, one is completely no echo, I can not know whether I write success, second, I can not predict the cause of failure, it is possible that Redis has a password, or Redis is normal permissions, or the Config set command is disabled, and so on.

Feeling is also a comparison of the situation of egg pain and chicken.

0x04 Python anti-serialization counter attack

Sure enough, the environment in the online attempt to write to the cron file did not bounce back to the shell successfully.

Stuck in this place for a long time, thinking has been considering "whether the file is really successful," if "successfully written to the file", why did not bounce to the shell, if not successfully written to the file, is not a permission to write to the Python Webshell? A few ideas are summed up:

    1. Write SSH key for Getshell. However, sweep port discovery does not seem to be open 22, presumably to replace the SSH port and the IP restrictions, or directly do not run sshd.
    2. The write Cron tries to bounce the shell, but it does not succeed. Perhaps Redis has no permissions, perhaps because the target is Ubuntu or Debian, these two systems for cron file format restrictions will be more stringent, it is difficult to use Redis rebound shell.
    3. Writes to Python's Webshell, but may also encounter file format requirements that are too restrictive to cause Python to fail, and often write to Python scripts that require a server restart to work
    4. Writes the JINJA2 template file and executes the command through the syntax supported by the template engine.

Summing up, the 4th method is the most reliable, because the template file is not strict format requirements, as long as I need to execute the statement placed in a similar {{ }} tag. But after testing, there are still a few problems: first, the Web path (the key is to store the template file path) and template name all need to guess, this is too difficult; second, Redis if it is installed from the source, typically Redis users run, generally cannot write to the Web directory.

Eat a supper and come back to think about it, I think the first thing to do is to solve the problem of "whether the file is actually successfully written". After fuzz a bit, tested a bunch of directories, found that the /var/www/html/static file was successfully written down, and was http://example.com/static/xxxfile directly accessible!

Download the file you just wrote, this file is the Redis export file. I've introduced it to my local redis environment, and it's a surprise:

Seeing this style of data, I knew that this must be the deserialized data, and it is the Python2.7 of the deserialized data.

Here the popular science, Python2.7 and 3.5 by default, the serialization format is different, generally with parentheses and line wrapping of the serialized data is 2.7 used, and the inclusion \x00 of the general is 3.5 used.

Follow-up use and https://www.leavesongs.com/PENETRATION/zhangyue-python-web-code-execute.html this article a routine. The target station uses Redis to store session data, and session data is saved using serialization, and we can execute arbitrary commands by deserializing them.

Use python2.7 to construct a cookie that executes the serialized data of the bounce shell script and sets the value by the SSRF vulnerability and session:hacker then accesses the target site session=hacker .

One thing to note, though, is that the URL is too long to throw an error (said before the local test), so you need a ssrf, use the Redis append command, write the data a paragraph, similar to this:

There is also a pit, when written, special characters (such as line breaks) need to be escaped: and http://127.0.0.1%0d%0aAPPEND%20session:hacker%20"(S‘id‘\np1\ntp2\nRp3\n."%0d%0aSAVE%0d%0a:6379/ only the escape character is escaped when the value is wrapped in quotation marks, or the escape character is escaped ... This put me in the pit for a long time, almost thought fall short.

Finally feel, dig holes thinking still have to jump, have been considering how to write files through Redis Getshell, but did not think by reading Redis backup files, found a breach.

Successful rebound Shell:

Summarize

In this case, there are several root causes of the vulnerability:

    1. SSRF vulnerability at the web level
    2. Python version is too low, there is a cve-2016-5699 header injection vulnerability
    3. Redis version is too low, the new Redis write file permissions are generally 660, can greatly avoid the vulnerability of writing files

After I got the shell, I looked at the source code, the logic is this: Get the user incoming URL parameters, send the HTTP request directly and get the return object, determine whether the return object's Content-type contains an image, if it contains the offline data and display, otherwise return error.

This causes the HTTP request to appear in error, the server throws 500, and the return error is the result of manual judgment, so the status code is 200.

There is also a feeling that I am afraid to ruin the target environment, the whole process has been used in the local environment for testing, and all the local environment is Docker-initiated, very convenient.

Then I should simulate this goal, do a vulhub environment for everyone, have time to say it ...

Python security-from SSRF to command execution massacre

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.