Pentesting
Modern Web
Apps: A Primer
Brian D. Hysell
About me
 Work in Threat & Vulnerability
Management (pentesting, vulnerability
assessment / management, threat
modelling, etc.) at Accenture Security
out of Atlanta office
 Mostly do web app assessment /
pentesting work
 Like making ice cream, baking,
cooking
 Like bad movies like The Room, Miami
Connection, Samurai Cop, and
R.O.T.O.R.
 Don't like writing "About me" slides
Motivation
 Most introductory resources in web appsec focus on old-school app
architectures
 The goal: look at issues and approaches pertinent to modern apps, but still
keep things basic
 "Modern":
 Implements large portion of presentation and business logic with frontend
framework like Angular[JS], Vue, or React
 Communicates with server via API, usually RESTful
 Not: uses this month's hot new state management lib
 Based on my own experience – I don't talk about React much not because it's
not important, but because I haven't dealt with it much in client work
XSS in
Frontend
Frameworks
Data binding
 Frameworks like AngularJS & Vue
do a lot, but what we're most
interested in here is data binding
 Instead of making manual edits to
the DOM, developers can use
client-side templates to
interpolate data into the page
https://jsfiddle.net/4nxwyd3y/18/
Default protection
 They protect against XSS by
default: data bindings (e.g., via {{
}} expression syntax) are
automatically encoded
 Two main ways for an application
to nonetheless be vulnerable:
 Encoding mechanisms intentionally
disabled
 Client-side template injection
https://jsfiddle.net/4nxwyd3y/18/
Disabled protections
 At some point, devs will want to
insert raw HTML
 Frameworks provide means to
accommodate this need
 Raw HTML + unescaped user input
= XSS, as always
Framework Example sink
AngularJS
(1.x)
$sce.trustAsHtml, ng-bind-
html-unsafe (removed in
1.2)
Angular (2+) bypassSecurityTrustHtml
Vue Triple curly braces (v1
only), v-html
React dangerouslySetInnerHTML
Vue Example: v-html
https://jsfiddle.net/4nxwyd3y/15/
Client-side template injection
 Applications – especially legacy
ones with a new coat of paint –
may mix client- and server-side
templating
 Server-side may have point where
untrusted user input is reflected
 Recall:
 A server-side templating engine
will generally encode <, >, ', and "
 The client-side framework will use
{{ and }} to demarcate expressions
 Hmm…
Resources
XSS without HTML: Client-Side
Template Injection with AngularJS
So you thought you were safe using
AngularJS. . . . Think again!
ng-owasp: OWASP Top 10 for AngularJS
Applications
Vue.js serverside template XSS
example
Exploiting Script Injection Flaws in
ReactJS Apps
Client-side template injection
 AngularJS expressions are
"JavaScript-esque"
 Can access object properties,
perform simple operations, etc.
 No access to important globals like
document and window by default,
but…
 Previous versions had sandbox
meant to prevent access, but
security researchers produced a
litany of escapes
 1.6 removed sandbox entirely
Resources
AngularJS Developer Guide:
Expressions
AngularJS 1.6 Expression Sandbox
Removal
Client-side template injection
https://jsfiddle.net/8xL6g2va/
Expression breakdown
{} An object
{}.toString A function
{}.toString.constructor The function constructor
{}.toString.constructor('js here') A function dynamically constructed
from a string
{}.toString.constructor('js here')() Said function, called
Client-side template injection (in Vue)
Same works in Vue
https://jsfiddle.net/4nxwyd3y/10/
What about quotation marks?
 Our expression doesn't need angle brackets, but what if quotation marks are
filtered/encoded, as they should be (and in my experience, have been)?
 Use ` backticks (new ECMAScript feature, template literals)? Nope,
expressions are JS-ish-but-not-JS, so AngularJS won't parse `
 Use String.fromCharCode? Nope, can't access String global object in
expression
 But wait, can't we just do the same {}.toString.constructor footwork to
replace String?
Expression breakdown – string without
quotes?
{} An object
{}.toString A function
{}.toString.constructor The function constructor (i.e., not
String)
{}.toString.constructor.fromCharCode() A call to fromCharCode()
Disclaimer…
Quoteless strings & the joys of odd type
conversions
![] The Boolean value false (I don't get it
either…)
[] An empty array… or an empty string???
+ Addition… or string concatenation with
funky automatic type conversion
![]+[] Boolean gets converted to string to be
concatenated with empty string, and
now we have the string "false"
• I'm not a JS expert – this explanation probably has issues
• Recommended reading: Web Application Obfuscation by Heiderich, Nava,
Heyes, and Lindsay
Expression breakdown – the cool way
![]+[] "false"
(![]+[]).constructor The String constructor
(![]+[]).constructor.fromCharCode The fromCharCode method
(![]+[]).constructor.fromCharCode(97,1
08,101,114,116,40,100,111,99,117,109,
101,110,116,46,100,111,109,97,105,11
0,41)
"alert(document.domain)"
All together now
https://jsfiddle.net/4nxwyd3y/19/
Just for fun (or WAF evasion?):
you can also replace:
{}.toString.constructor
with
(![]+[]).sub.constructor
The simple way
 If {}.toString gets us the
toString function, {}.toString()
should get us an empty string
 I tried it before and it didn't work,
I swear… apparently I tried it
incorrectly…
 It does work, and the "cool way" is
unnecessary 
https://jsfiddle.net/4nxwyd3y/19/
Enumeration
Rough typical app structure
Frontend
(presentation, business logic)
Angular[JS]
Vue
React
REST API
(business logic)
Jersey
Express
ASP.NET Web API
DB
(persistence)
Oracle
MongoDB
MS-SQL
Poking at frontend
code
 There's a fair chance all/most
modules in the app have been
mushed together into one file
via Webpack or similar
 This could include logic & API
calls meant for other roles
 So look in source for potentially
juicy XHRs (easier said than
done…)
 Some apps may also use
AngularJS's $resource service
REST API docs
 If available, it's useful to get a
copy of API docs, esp. in
Swagger/OpenAPI or WADL format
 Docs not necessarily available by
default
 APIs following the HATEOAS
constraint may be partially self-
documenting
Framework Common doc path
Generic /, /doc, /v1/api/doc, etc.
Jersey /application.wadl[detail=true],
/<resource>/application.wadl
RESTEasy /application.xml
CXF /<WAR>/<servlet>/services,
/<WAR>/<servlet>/<resource>/
?_wadl
Spring w/
Springfox
/v2/api-docs
ASP.NET Web
API w/
Swashbuckle
/swagger/docs/v1
Random example (inurl:application.wadl)
https://api.flightstats.com/flex/flightstatus/rest/application.wadl
Logic &
Authz
Testing
REST in 10 seconds
Get a list of books GET /api/books
Create a book POST /api/books
{"title": "Based on a True Story", "author":
"Norm MacDonald"}
Retrieve a book GET /api/books/243
Edit a book PUT /api/books/243
{"title": "Based", "author": "Norm"}
Delete a book DELETE /api/books/243
Logic vulnerabilities
 Since more app logic is handled client-side, there are more opportunities for
devs to forget to check security-relevant logic server-side
 Simple operations like displaying the name of the logged-in user in the upper-right
can involve API calls (e.g., GET /users/current)
 While plenty of apps do it "incorrectly", REST APIs generally operate on
"resources" (like a user) rather than specifying particular "actions" (like
changing a user's email)
 Operations that might obviously need to be restricted as "actions" – or never
be made available at all – may slip through the cracks as CRUD operations on
"resources"
Logic vulnerabilities
Real-world examples
User's role could be elevated from unprivileged to admin with a PUT to
/users/current
Unprivileged users could POST to /users and create new (admin) users
Answers to user's password-reset questions could be retrieved with GET to
/users/<ID>
User could change another user's password reset code – and hijack that user's
account – with a PATCH to /users/<ID>
Postman
 If you find yourself hand-writing
API calls in Burp Repeater or
curl too much, try Postman
 Can import API docs if you have
them
 Postman environments and/or
collections ("inherit
authorization from parent")
features can be useful for
authn/authz testing
Misc
CORS background: the same-origin
policy
 JavaScript can use XMLHttpRequest (Ajax) calls to fetch the contents of the
page / response of an API call
 If the withCredentials property is set to true, cookies, auth headers, and client
certs will be sent
 What stops attacker.com from reading my data from gmail.com?
 Under the same-origin policy, JavaScript on attacker.com is forbidden from
reading responses to Ajax requests made to gmail.com
 Origin: (scheme, hostname, port)
 E.g., (https, gmail.com, 443)
Cross-origin resource sharing
 Developers can override this behavior with
CORS
 It is a bit more involved than this, but if:
 brian.com made an Ajax call to
GET
https://hysell.com/api/private
 hysell.com responded with the
header Access-Control-Allow-
Origin: https://brian.com
 hysell.com responded with the
header Access-Control-Allow-
Credentials: true
 …then the browser would send the request
with cookies etc. & allow brian.com to
read the response
CORS pitfalls
 Browser sends "Origin" header with request (e.g., Origin:
https://attacker.com)
 Sometimes misconfigured apps reflect back value of request's Origin header in
value of Access-Control-Allow-Origin
 When this is the case & Access-Control-Allow-Credentials is true, third-party
sites can read API data of logged-in users with impunity!
 Caveat: only applies to credentials handled by the browser like client certs,
cookies, or HTTP basic auth headers; not, e.g., to bearer tokens stored in a header
Framework vulnerabilities
 Applications will often use some
kind of framework or plugin to
build their REST APIs, e.g., Restlet,
RESTEasy, Jersey, or CXF
 Just because the frontend JS talks
to the API in JSON doesn't mean
that's all the REST backend
understands
 Frameworks will often accept XML,
and sometimes even more exotic
inputs like YAML or even serialized
Java objects (!!!)
Framework Input CVE
Struts REST XML CVE-2017-
9805
RESTEasy YAML CVE-2016-
9606
RESTEasy Serialized
objects
CVE-2016-
7050
Restlet XML CVE-2017-
14868, CVE-
2017-14949
Framework vulnerabilities
 Frameworks may pass YAML, XML,
etc. to insecure object
unmarshallers
 E.g., the recent Struts REST RCE –
XML was handled by the XStream
unmarshaller, which can be
leveraged to create dangerous Java
objects
 Older (…and not so much older)
frameworks may have XXE
vulnerabilities processing XML
Resources
Entity provider selection confusion
attacks in JAX-RS applications
Unsafe JAX-RS: Breaking REST API
Java Unmarshaller Security
Q&A

Pentesting Modern Web Apps: A Primer

  • 1.
    Pentesting Modern Web Apps: APrimer Brian D. Hysell
  • 2.
    About me  Workin Threat & Vulnerability Management (pentesting, vulnerability assessment / management, threat modelling, etc.) at Accenture Security out of Atlanta office  Mostly do web app assessment / pentesting work  Like making ice cream, baking, cooking  Like bad movies like The Room, Miami Connection, Samurai Cop, and R.O.T.O.R.  Don't like writing "About me" slides
  • 3.
    Motivation  Most introductoryresources in web appsec focus on old-school app architectures  The goal: look at issues and approaches pertinent to modern apps, but still keep things basic  "Modern":  Implements large portion of presentation and business logic with frontend framework like Angular[JS], Vue, or React  Communicates with server via API, usually RESTful  Not: uses this month's hot new state management lib  Based on my own experience – I don't talk about React much not because it's not important, but because I haven't dealt with it much in client work
  • 4.
  • 5.
    Data binding  Frameworkslike AngularJS & Vue do a lot, but what we're most interested in here is data binding  Instead of making manual edits to the DOM, developers can use client-side templates to interpolate data into the page https://jsfiddle.net/4nxwyd3y/18/
  • 6.
    Default protection  Theyprotect against XSS by default: data bindings (e.g., via {{ }} expression syntax) are automatically encoded  Two main ways for an application to nonetheless be vulnerable:  Encoding mechanisms intentionally disabled  Client-side template injection https://jsfiddle.net/4nxwyd3y/18/
  • 7.
    Disabled protections  Atsome point, devs will want to insert raw HTML  Frameworks provide means to accommodate this need  Raw HTML + unescaped user input = XSS, as always Framework Example sink AngularJS (1.x) $sce.trustAsHtml, ng-bind- html-unsafe (removed in 1.2) Angular (2+) bypassSecurityTrustHtml Vue Triple curly braces (v1 only), v-html React dangerouslySetInnerHTML
  • 8.
  • 9.
    Client-side template injection Applications – especially legacy ones with a new coat of paint – may mix client- and server-side templating  Server-side may have point where untrusted user input is reflected  Recall:  A server-side templating engine will generally encode <, >, ', and "  The client-side framework will use {{ and }} to demarcate expressions  Hmm… Resources XSS without HTML: Client-Side Template Injection with AngularJS So you thought you were safe using AngularJS. . . . Think again! ng-owasp: OWASP Top 10 for AngularJS Applications Vue.js serverside template XSS example Exploiting Script Injection Flaws in ReactJS Apps
  • 10.
    Client-side template injection AngularJS expressions are "JavaScript-esque"  Can access object properties, perform simple operations, etc.  No access to important globals like document and window by default, but…  Previous versions had sandbox meant to prevent access, but security researchers produced a litany of escapes  1.6 removed sandbox entirely Resources AngularJS Developer Guide: Expressions AngularJS 1.6 Expression Sandbox Removal
  • 11.
  • 12.
    Expression breakdown {} Anobject {}.toString A function {}.toString.constructor The function constructor {}.toString.constructor('js here') A function dynamically constructed from a string {}.toString.constructor('js here')() Said function, called
  • 13.
    Client-side template injection(in Vue) Same works in Vue https://jsfiddle.net/4nxwyd3y/10/
  • 14.
    What about quotationmarks?  Our expression doesn't need angle brackets, but what if quotation marks are filtered/encoded, as they should be (and in my experience, have been)?  Use ` backticks (new ECMAScript feature, template literals)? Nope, expressions are JS-ish-but-not-JS, so AngularJS won't parse `  Use String.fromCharCode? Nope, can't access String global object in expression  But wait, can't we just do the same {}.toString.constructor footwork to replace String?
  • 15.
    Expression breakdown –string without quotes? {} An object {}.toString A function {}.toString.constructor The function constructor (i.e., not String) {}.toString.constructor.fromCharCode() A call to fromCharCode()
  • 16.
  • 17.
    Quoteless strings &the joys of odd type conversions ![] The Boolean value false (I don't get it either…) [] An empty array… or an empty string??? + Addition… or string concatenation with funky automatic type conversion ![]+[] Boolean gets converted to string to be concatenated with empty string, and now we have the string "false" • I'm not a JS expert – this explanation probably has issues • Recommended reading: Web Application Obfuscation by Heiderich, Nava, Heyes, and Lindsay
  • 18.
    Expression breakdown –the cool way ![]+[] "false" (![]+[]).constructor The String constructor (![]+[]).constructor.fromCharCode The fromCharCode method (![]+[]).constructor.fromCharCode(97,1 08,101,114,116,40,100,111,99,117,109, 101,110,116,46,100,111,109,97,105,11 0,41) "alert(document.domain)"
  • 19.
    All together now https://jsfiddle.net/4nxwyd3y/19/ Justfor fun (or WAF evasion?): you can also replace: {}.toString.constructor with (![]+[]).sub.constructor
  • 20.
    The simple way If {}.toString gets us the toString function, {}.toString() should get us an empty string  I tried it before and it didn't work, I swear… apparently I tried it incorrectly…  It does work, and the "cool way" is unnecessary  https://jsfiddle.net/4nxwyd3y/19/
  • 21.
  • 22.
    Rough typical appstructure Frontend (presentation, business logic) Angular[JS] Vue React REST API (business logic) Jersey Express ASP.NET Web API DB (persistence) Oracle MongoDB MS-SQL
  • 23.
    Poking at frontend code There's a fair chance all/most modules in the app have been mushed together into one file via Webpack or similar  This could include logic & API calls meant for other roles  So look in source for potentially juicy XHRs (easier said than done…)  Some apps may also use AngularJS's $resource service
  • 24.
    REST API docs If available, it's useful to get a copy of API docs, esp. in Swagger/OpenAPI or WADL format  Docs not necessarily available by default  APIs following the HATEOAS constraint may be partially self- documenting Framework Common doc path Generic /, /doc, /v1/api/doc, etc. Jersey /application.wadl[detail=true], /<resource>/application.wadl RESTEasy /application.xml CXF /<WAR>/<servlet>/services, /<WAR>/<servlet>/<resource>/ ?_wadl Spring w/ Springfox /v2/api-docs ASP.NET Web API w/ Swashbuckle /swagger/docs/v1
  • 25.
  • 26.
  • 27.
    REST in 10seconds Get a list of books GET /api/books Create a book POST /api/books {"title": "Based on a True Story", "author": "Norm MacDonald"} Retrieve a book GET /api/books/243 Edit a book PUT /api/books/243 {"title": "Based", "author": "Norm"} Delete a book DELETE /api/books/243
  • 28.
    Logic vulnerabilities  Sincemore app logic is handled client-side, there are more opportunities for devs to forget to check security-relevant logic server-side  Simple operations like displaying the name of the logged-in user in the upper-right can involve API calls (e.g., GET /users/current)  While plenty of apps do it "incorrectly", REST APIs generally operate on "resources" (like a user) rather than specifying particular "actions" (like changing a user's email)  Operations that might obviously need to be restricted as "actions" – or never be made available at all – may slip through the cracks as CRUD operations on "resources"
  • 29.
    Logic vulnerabilities Real-world examples User'srole could be elevated from unprivileged to admin with a PUT to /users/current Unprivileged users could POST to /users and create new (admin) users Answers to user's password-reset questions could be retrieved with GET to /users/<ID> User could change another user's password reset code – and hijack that user's account – with a PATCH to /users/<ID>
  • 30.
    Postman  If youfind yourself hand-writing API calls in Burp Repeater or curl too much, try Postman  Can import API docs if you have them  Postman environments and/or collections ("inherit authorization from parent") features can be useful for authn/authz testing
  • 31.
  • 32.
    CORS background: thesame-origin policy  JavaScript can use XMLHttpRequest (Ajax) calls to fetch the contents of the page / response of an API call  If the withCredentials property is set to true, cookies, auth headers, and client certs will be sent  What stops attacker.com from reading my data from gmail.com?  Under the same-origin policy, JavaScript on attacker.com is forbidden from reading responses to Ajax requests made to gmail.com  Origin: (scheme, hostname, port)  E.g., (https, gmail.com, 443)
  • 33.
    Cross-origin resource sharing Developers can override this behavior with CORS  It is a bit more involved than this, but if:  brian.com made an Ajax call to GET https://hysell.com/api/private  hysell.com responded with the header Access-Control-Allow- Origin: https://brian.com  hysell.com responded with the header Access-Control-Allow- Credentials: true  …then the browser would send the request with cookies etc. & allow brian.com to read the response
  • 34.
    CORS pitfalls  Browsersends "Origin" header with request (e.g., Origin: https://attacker.com)  Sometimes misconfigured apps reflect back value of request's Origin header in value of Access-Control-Allow-Origin  When this is the case & Access-Control-Allow-Credentials is true, third-party sites can read API data of logged-in users with impunity!  Caveat: only applies to credentials handled by the browser like client certs, cookies, or HTTP basic auth headers; not, e.g., to bearer tokens stored in a header
  • 35.
    Framework vulnerabilities  Applicationswill often use some kind of framework or plugin to build their REST APIs, e.g., Restlet, RESTEasy, Jersey, or CXF  Just because the frontend JS talks to the API in JSON doesn't mean that's all the REST backend understands  Frameworks will often accept XML, and sometimes even more exotic inputs like YAML or even serialized Java objects (!!!) Framework Input CVE Struts REST XML CVE-2017- 9805 RESTEasy YAML CVE-2016- 9606 RESTEasy Serialized objects CVE-2016- 7050 Restlet XML CVE-2017- 14868, CVE- 2017-14949
  • 36.
    Framework vulnerabilities  Frameworksmay pass YAML, XML, etc. to insecure object unmarshallers  E.g., the recent Struts REST RCE – XML was handled by the XStream unmarshaller, which can be leveraged to create dangerous Java objects  Older (…and not so much older) frameworks may have XXE vulnerabilities processing XML Resources Entity provider selection confusion attacks in JAX-RS applications Unsafe JAX-RS: Breaking REST API Java Unmarshaller Security
  • 37.