The security of an application is a continuous struggle between solid proactive controls and quality in SDLC versus human weakness and resource restrictions. As the pentester's experience confirms, unfortunatelly even in high-risk (e.g. banking) applications, developed by recognized vendors, the latter often wins - and we end up with critical vulnerabilities.
One of the primary reasons is lack of mechanisms enforcing secure code by default, as opposed to manual adding security per each function. Whenever the secure configuration is not default, there will almost inevitably be bugs, especially in complex systems.
I will pinpoint what should be taken into consideration in the architecture and design process of the application. I will show solutions that impose security in ways difficult to circumvent unintentionally by creative developers. I will also share with the audience the pentester's (=attacker's) perspective, and a few clever tricks that made the pentest
(=attack) painful, or just rendered the scenarios irrelevant.
2. Pentester / security consultant.
Assessments and consultancy regarding
security of various applications - web,
mobile, embedded, ...
Since 2003 / over 400 systems and
applications
Sławomir Jasek
3. Code insecure by default
Blacklisting vs whitelisting
Features vs security
Access control
Beware the "silver bullets"
Fight back!
Devops
The Takeaway
Agenda
11. // Open SSLSocket directly
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket("mail.google.com", 443);
SSLSession s = socket.getSession();
// ... use socket ...
socket.close();
Java: SSL SocketFactory
12. // Open SSLSocket directly
SocketFactory sf = SSLSocketFactory.getDefault();
SSLSocket socket = (SSLSocket) sf.createSocket("mail.google.com", 443);
SSLSession s = socket.getSession();
// Verify that the certicate hostname is for mail.google.com
HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
if (!hv.verify("mail.google.com", s)) {
throw new SSLHandshakeException("Expected mail.google.com, "
"found " + s.getPeerPrincipal());
}
// ... use socket ...
socket.close();
https://developer.android.com/training/articles/security-ssl.html#WarningsSslSocket
SSL SocketFactory
13. And the docs do not help
https://developer.android.com/training/articles/security-ssl.html#WarningsSslSocket
Pentester’s experience: all tested Android apps using
SSLSocket were vulnerable.
Despite the bold warnings and proper example code...
14. URL url = new URL("https://mail.google.com");
URLConnection urlConnection = url.openConnection();
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
You need to explicitly disable verification.
Pentester’s experience: only a few tested Android apps
using urlConnection were deliberately broken.
HTTPS – the proper way
18. Attack 1:
/myAction.action?param"><sCript>alert('XSS');</sCript>=1
This is very similiar to the vulnerability in Security Bulletin S2-002; however,
the implemented fix for S2-002 only checks for "<script>", not "<sCript>".
Attack 2:
/myAction.action?param"onMouseOver%3D"javascript:alert('XSS');">=1
Simply checking for <script> isn't sufficient because certain attributes can be
injected to execute javascript. In attack 2, the user simply has to hover over
the link with their mouse and arbitrary javascript will be executed.
https://issues.apache.org/jira/browse/WW-3410
2 years later...
20. We found an XSS vulnerability, as usually recommended
to properly encode relevant characters in the output
context.
Retest #1: <script> does not work, but <Script> does ;)
Retest #2: <Script> nor <sCript> does not work. But onclick does.
Retest #3: onclick does not work, but onmouseover does.
Retest #4: onmouseover fixed, but onmousedown not ;)
Retest #5: ...
From the pentester's diary
24. Define user
# fields: [:id, :first, :last, :email, :admin]
class User < ActiveRecord::Base
End
RoR – user edit form
User edit form
user = User.find(params[:id])
user.update_attributes(params[:user])
27. You were supposed to manually add whitelisted:
class User < ActiveRecord::Base
attr_accessible :first, :last, :email
end
or blacklisted parameters:
class User < ActiveRecord::Base
attr_protected :admin
end
RoR – mass assignment (2012)
29. Change default value of global config parameter turning
the mass assignment off:
# Enforce whitelist mode for mass assignment.
# This will create an empty whitelist of attributes available for mass-
assignment for all models
# in your app. As such, your models will need to explicitly whitelist or
blacklist accessible
# parameters by using an attr_accessible or attr_protected declaration.
<%= comment_if :skip_active_record %>
config.active_record.whitelist_attributes = true
Fix - evolution
31. Secure by default. You must first call permit on the params hash
with the keys that are allowed for a specific action:
# Require that :user be a key in the params Hash,
# and only accept whitelisted attributes
def user_params
params.require(:user).permit(:first, :last, :email)
end
http://code.tutsplus.com/tutorials/mass-assignment-rails-and-you--net-31695
RoR 4: Strong Parameters
35. Major frameworks: Spring, JBoss
SEAM, Struts...
Easy to detect, automatic tools
to exploit remotely into shell.
The "no man's land".
(as I have pointed out in 2012)
http://prezi.com/awm8psp-i1ok/no-mans-land
Expression Language flaws
https://play.google.com/store/apps/details?id=com.cleanmaster.security.struts2
36. 2003: parameter names like: @System@exit(1)
"Patrick says he has fixed it" ;)
2008.06: the # can be encoded in u0023
('u0023' + 'session['user'])(unused)=0wn3d
Released Xwork 2.0.5 – blacklisted the attack
2008.10: removed space characters and the exploit still
works ;)
('u0023'+'session['user'])(unused)=0wn3d
The fix - features vs security
37. 2010: You can access the context and turn the settings on:
http://mydomain/MyStruts.action?('u0023_memberAccess['allowStatic
MethodAccess']')(meh)=true&(aaa)(('u0023context['xwork.MethodAcc
essor.denyMethodExecution']u003du0023foo')(u0023foou003dnew%20
java.lang.Boolean("false")))&(asdf)(('u0023rt.exit(1)')(u0023rtu
003d@java.lang.Runtime@getRuntime()))=1
Fix: whitelist allowed chars in parameter names
2011: User input is evaluated as an OGNL expression when there's a
conversion error.
2011: The problem concerns not only parameters, but also cookie
values
The fix – continued...
38. 2012: fix based on whitelisting acceptable parameter names closed
the vulnerability only partially. Still possible RCE with slightly modified
attack syntax.
Fix: deny evaluation in parameter names.
2013: The second evaluation happens when redirect result reads it
from the stack and uses the previously injected code as redirect
parameter.
I won't reveal all the following episodes, I encourage you to read on:
http://struts.apache.org/docs/security-bulletins.html
There's a lot of action! Struts.action.
The fix – continued
44. <!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>
As seen in Google, Facebook, Ebay and about 80%
of tested applications processing user input as XML.
It may be even worse
46. Technology Default DTD processing value
.NET 4 settings.DtdProcessing =
DtdProcessing.Prohibit;
.NET 3.5 ProhibitDtd in XmlReaderSettings is true, but
ProhibitDtd in XmlTextReader is false
LibXML2 (C++ ) starting with libxml2 version 2.9 (2012), XXE has been disabled
by default
NSXMLDocument
(iOS)
iOS5 and later: Only entities that don't require network access
are loaded.
iOS4 and earlier: All external entities are loaded by default.
Xerces 2 disallow-doctype-decl=false
Xerces 1 external-general-entities=true
PHP Have to manually disable:
libxml_disable_entity_loader(true);
Default DTD processing in various parsers
49. Student tries to invoke administrative functions
GET /course/quiz/solutions
GET /admin/course/cancel
Student tries to access restricted data
GET /course/view/42
Student tries to alter his account rights.
POST /user/edit
roles=[student,admin]
Access control: typical attack
50. <security:intercept-url pattern="/user/add"
access="hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')" />
<security:intercept-url pattern="/user/view"
access="hasRole('ROLE_ADMIN')
or hasRole('ROLE_MANAGER')
or hasRole('ROLE_PRINCIPAL')
or hasRole('ROLE_TEACHER')
or hasRole('ROLE_STUDENT')" />
Approach 1: Spring Security
51. It works for simple apps, with unsophisticated role policies
(e.g. separated admin interface).
Will not work for:
POST /user/edit HTTP/1.1
task=MODIFY_RIGHTS&id=34
Has concept of „roles”, but out of the box does not have
concept of „permissions”. Spring ACL on the other hand is
too complex.
Does not help a lot with access to specific instance (e.g.
other user’s data)
Spring Security
52. Complex hard-coded checks in application code,
needed to be manually loaded to every endpoint:
If (( user.isRole('ROLE_ADMIN') ||
user.isRole('ROLE_MANAGER') ||
user.isRole('ROLE_PRINCIPAL') ) ||
( user.isRole('ROLE_TEACHER') &&
user.isTeacher(course) ) ||
( user.isRole('ROLE_STUDENT') &&
(user.isStudent(course) )
...and you will probably end-up with:
53. One simple "if" statement, permissions separated from roles.
if ( currentUser.isPermitted("users:add") ) {
//add an user
}
int courseId=request.getInt("course")
if ( currentUser.isPermitted("courses:view:"+courseId) ) {
//show contents of course
}
Apache Shiro – permission based access control
55. The REST API request
GET /services/history/account/85101022350445200448009906 HTTP/1.1
SA-DeviceId: 940109f08ba56a89
SA-SessionId: 826175
Accept: application/json
Host: acc
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
56. The REST API request
GET /services/history/account/45101022350445200448005388 HTTP/1.1
SA-DeviceId: 940109f08ba56a89
SA-SessionId: 826175
Accept: application/json
Host: acc
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
Change the acc number -> get other user’s data
57. John:
<select name="account">
<option value=0 >85101022350445200448009906</option>
<option value=1 >34101022350445200448009905</option>
<option value=2 >41101022350445200448009904</option>
</select>
Mary:
<select name="account">
<option value=0 >45101022350445200448005388</option>
<option value=1 >31101022350445200448005390</option>
</select>
The better way?
"Local" ID mapped server-side
into real values
58. The better way?
SA-DeviceId: d4c79a0fd994b1f3
SA-SessionId: 850073
GET /services/history/account/1 HTTP/1.1
SA-DeviceId: 940109f08ba56a89
SA-SessionId: 826175
Accept: application/json
Host: acc
Connection: Keep-Alive
User-Agent: Apache-HttpClient/UNAVAILABLE (java 1.4)
It is(?) by design not possible to attack other user's data.
59. Would be great if not...
John:
GET /services/history/account/2
HTTP/1.1
SA-DeviceId: 940109f08ba56a89
SA-SessionId: 826175
Accept: application/json
Host: acc
Connection: Keep-Alive
API just works as a proxy to backend system.
Default session mechanism from backend – just incrementing IDs ;)
Mary:
GET /services/history/account/2
HTTP/1.1
SA-DeviceId: d4c79a0fd994b1f3
SA-SessionId: 826179
Accept: application/json
Host: acc
Connection: Keep-Alive
Trivial to guess other user's sessionId
60. Automatically encrypts paths with individual, session-
based and unique key:
www.app.com/admin/user/edit
www.app.com/?x=XSn4MUyv6Uke22d9vEuGgR8yZa5-
TkeuxlXeeVypcehJ8HQgNbj(...)
Apache Wicket
61. The browser works on rendered GUI elements, POSTs to the
server event-driven actions (e.g. mouse clicks in certain location).
It is by design not possible to invoke actions that are not in GUI.
POST /dashboard/UIDL/?v-uiId=0 HTTP/1.1
Host: demo.vaadin.com
{"csrfToken":"981b1cd6-66df-481c-9d6e-
6c293eb70ea3","rpc":[["283","v","v",["positionx",["i","440"]]],["283","
v","v",["positiony",["i","139"]]],["309","com.vaadin.shared.ui.button.B
uttonServerRpc","click",[{"altKey":false,"relativeX":"42","relativeY":"
23","ctrlKey":false,"button":"LEFT","shiftKey":false,"clientX":"1109","
clientY":"598","metaKey":false,"type":"1"}]]],"syncId":14}
Vaadin
64. Automagic output encoding by framework
A framework automatically encodes special chars into
HTML:
<bean:write name="transferFormId" property="trn_recipient"/>
ATTACK: trn_recipient="<script>alert('xss')</script>
<input type="text" name="trn_recipient"
value=""<script>alert('xss')</script>"
65. Beware the security mechanism use cases
But unfortunatelly that does not help when you put a
value from end-user directly into javascript context:
<script> var split='<bean:write name="transferFormId"
property="trn_recipient">'; splitRecipient(split);
</script>
ATTACK: trn_recipient=';alert('xss');--
<script> var split='';alert('xss');--
66. Encode special chars properly in context!
• HTML element
• HTML atribute
• JavaScript
• JSON
• CSS / style
• URL
Encode properly
68. Called stored procedure
PROCEDURE search(
p_firstname IN T_STRING,
p_lastname IN T_STRING,
) IS
(...)
v_sql_select := ' SELECT distinct a.USER_ID';
v_sql_from := ' FROM APP_WEB.USERS a ';
v_sql_where := ' WHERE a.USER_ID is not null ';
IF p_firstname is not null THEN
v_sql_where := v_sql_where || ' and lower(trim(a.FIRSTNAME)) =
lower(trim(' || P_FIRSTNAME || ')) ';
END IF;
69. SQL injection inside stored procedure
PROCEDURE search(
p_firstname IN T_STRING,
p_lastname IN T_STRING,
) IS
(...)
v_sql_select := ' SELECT distinct a.USER_ID';
v_sql_from := ' FROM APP_WEB.USERS a ';
v_sql_where := ' WHERE a.USER_ID is not null ';
IF p_firstname is not null THEN
v_sql_where := v_sql_where || ' and lower(trim(a.FIRSTNAME)) =
lower(trim('|| 'adam')) union select version,'x' from v$instance-- || ')) ';
END IF;
70. NoSQL. There is no sql injection.
There is nosql injection!
Instead of
' OR 1=1 --
try
a'; return 1=1; var dummy='a
NoSQL
72. What could possibly go wrong?
Change SMS authorization phone number in internet banking application
73. The scenario missed in functional tests
Change SMS authorization phone number in internet banking application
74. The evil wins
The application did verify only the SMS code from the new phone.
And intruder with access to user session can take over the authorization.
76. • Input validation server-side when client-side
validation exists
• Non-user editable parameters/values (hidden fields,
checkboxes, radio buttons, select lists)
• Forced browsing to common attack entry points or
honeypot URL (e.g. in robots.txt)
• Obvious sqli, xss inj attacks
• Workflow sequence abuse
Intrusion detection – level basic
http://www.slideshare.net/JimManico/top-ten-defenses-v10 slide 60
77. One of the most annoying experiences during test was
automatic logout on every test-case in application that
required manual interaction to authenticate.
Active intrusion prevention
78. OWASP AppSensor
Conceptual framework and methodology to implement intrusion detection and
automated response into applications.
https://www.owasp.org/index.php/OWASP_AppSensor_Project
OWASP mod_security core ruleset
An easily "pluggable" set of generic attack detection rules that
provide a base level of protection for any web application.
https://www.owasp.org/index.php/Category:OWASP_ModSecurity_Core_Rule_Set_Project
Basic tools
95. OWASP dependency check / dependency track
Automatically checks for known
vulnerabilities in used components
https://www.owasp.org/index.php/OWASP_Dependency_Track
97. • Use secure design architecture
• Least privilege principle (default: deny)
• Code that enforces good practices
• Leverage existing security mechanisms, but be aware
of their shortcomings and secure use scenario.
• Secure configuration
• Keep the components up to date
• Change default credentials
• Harden the configuration
• Leverage additional layers of protection (IDS, WAF)
Key takeaways
98. Our presentations (including this one), resources
www.securing.pl/en/resources/
Free security consultancy service:
www.securing.pl/konsultacje
See also
99. And for the Happy(?)-End – the pentester’s view
Features at low cost compromising on security is just obscene ;) Let’s do it better!
100. Thank you,
looking forward to contact!
slawomir.jasek@securing.pl
MORE THAN
SECURITY
TESTING
Free security consultancy service:
www.securing.pl/konsultacje