April 2018, Budapest
Intrusions increase once users discover a vulnerability, and the rate continues to increase until the vendor releases (and system administrators install) a patch or workaround.
Completely new types of attacks are very rare!
OWASP Top 10 | |
---|---|
A1 | Injection |
A2 | Broken Authentication and Session Management |
A3 | Cross-Site Scripting (XSS) |
A4 | Insecure Direct Object References |
A5 | Security Misconfiguration |
A6 | Sensitive Data Exposure |
A7 | Missing Function Level Access Control |
A8 | Cross-Site Request Forgery (CSRF) |
A9 | Using Known Vulnerable Components |
A10 | Unvalidated Redirects and Forwards |
OWASP Top 10 | |
---|---|
A1 | Injection |
A2 | Broken Authentication and Session Management |
A3 | Sensitive Data Exposure |
A4 | XML External Entities (XXE) |
A5 | Broken Access Control Merged from A4 and A7 in 2013 list |
A6 | Security Misconfiguration |
A7 | Cross-Site Scripting (XSS) |
A8 | Insecure Deserialization |
A9 | Using Known Vulnerable Components |
A10 | Insufficient Logging and Monitoring |
User input is used as part of the input to a database
Typically these are SQL databases today
But problem applies to all kinds of DBs,
DB languages & inputs!
Typical examples: Login forms, search forms,
other forms
Commands without permission can be executed
$mysqli->query("SELECT * FROM users WHERE username = '$username' AND password='$hash'");
Username: admin';--
Password: <anything>
SELECT * FROM users WHERE username = 'admin';-- AND password='<hash>'
Sample authentication query:
Username: '; DROP TABLE users;--
Password: <anything>
SELECT * FROM users WHERE username = ''; DROP TABLE users;-- AND password='<hash>'
Wildcards are also useful: %(multi-), _ (single-char)
With SQL injection one can:
Possible without knowing the table or field names
Username: mate' AND 0<=(SELECT COUNT(*) FROM users);-
Password: <anything>
SELECT * FROM users
WHERE username = 'mate' AND 0<=(SELECT COUNT(*) FROM users);-- AND password='<hash>'
Succeeds only if table users exists and is in the current query.
Username: mate' AND users.username = 'mate';--
Password: <anything>
SELECT * FROM users
WHERE username = 'mate' AND users.username = 'mate';-- AND password='<hash>'
Succeeds only if table users exists.
Possible without seeing immediate results: blind injection
SELECT 1/0 FROM users WHERE username='admin';
Error only when such a user exists!
xmlobj.selectNodes("//users/admins/[user/text()='$username' and pass/text()='$hash']");
//users/admins/[user/text()='admin' or 1=1 and pass/text()='<hash>']
Same as SQL, but different database: XML files/XML-DB
Disadvantage: Comments are not possible. The query must always be correct as a whole.
Sample original query:
Username: admin' or 1=1
Password: anything
Data can also be sent to the application, which might later be interpreted as server-side code, e.g. as JSP/ASP/PHP code.
Logging is very important, but also needs security
The user can enter an email address, to which some data will be sent (recommendation etc.)
Possible input:
"sender@junk.com\nRCPT TO: rec1@org,rec2@org\n DATA\nSpam message\n.\nQUIT\n"
By just printing the user input as the destination address this will result in a "strange" SMTP session!
Basic idea:
How to prevent:
A complex attack to get a browser to accept a custom-crafted input as a webserver response
response.sendRedirect("/by_lang.php?lang="+request.getParameter("lang"));
HTTP/1.1 302 Moved Temporarily
Date: Wed, 24 Dec 2003 12:53:28 GMT
Location: http://10.1.1.1/by_lang.jsp?lang=English
Server: WebLogic XMLX Module 8.1 SP1 Fri Jun 20 23:06:40 PDT 2003 271009 with
Content-Type: text/html
Connection: Close
<html><head><title>302 Moved Temporarily</title></head>
<body bgcolor="#FFFFFF">
<p>This document you requested has moved temporarily.</p>
<p>It's now at
<a href="http://10.1.1.1/by_lang.php?lang=English">
http://10.1.1.1/by_lang.php?lang=English</a>.</p>
</body></html>
Sending parameter "English":
Source of example based on:
Amit Klein: "Divide and Conquer" – HTTP Response Splitting,
Web Cache Poisoning Attacks, and Related Topics, 2004
HTTP/1.1 302 Moved Temporarily
Date: Wed, 24 Dec 2003 15:26:41 GMT
Location: http://10.1.1.1/by_lang.php?lang=value
Content-Length: 0
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 19
<html>Attacking content</html>
Connection: Close
<html><head><title>302 Moved Temporarily</title></head>
Sending malicious parameter:
value CR LF HTTP-Headers CR LF CR LF HTTP-Headers CR LF CR LF Arbitrary content
value%0d%0a
Content-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0a
Content-Type:%20text/html%0d%0aContent-Length:%2019%0d%0a%0d%0a
<html>Attacking content</html>
Example:
Response:
Anything coming from the client might be used as a vehicle for injecting commands or scripts!
Code injection by malicious users into someone else's web
application, to be viewed/executed by end users
The URL is perfectly fine!
Server
Client
Attacker
Client
Target
1. Send in malicious input
Typically JavaScript
2. Request webpage
3. Response with malicious code inside
Reflected: Injecting a script which is “bounced” back
Stored: “store” the script on the site
XSS can do the following:
MySpace XSS worm, 2005: 1 million victims in <24 hours!
(Stored XSS, viewing an infected profile was enough.)
-1: Never insert JS code from another site into your page
0: Never insert untrusted data except in allowed locations
1: HTML-escape data before putting it into element content
2: Attribute-escape data before putting it into attributes
3: JavaScript-escape data before putting it in JS data values
4: CSS-escape data before putting it into style values
5: URL-escape data before putting it into URL parameters
Hello <?php echo $view->escape($name) ?>
var myMsg = "Hello <?php echo $view->escape($name, 'js') ?>";
Hello <?php echo htmlspecialchars ($name) ?>
HTML escaping with native PHP:
Most MVC frameworks have advanced solutions, e.g. with Symfony:
View templates can also support escaping, e.g. with Twig:
Hello {{ name }}
Hello {{ name|escape }} <!-- HTML escaped (htmlspecialchars) -->
JavaScript context escaping:
var myMsg = "Hello {{ name|escape('js') }}";
By default Symfony turns on autoescape for Twig.
By default, in a ASP.NET Core webpage using the Razor view engine HTML encodes all output with the @ syntax:
Hello @("<em>Mate</em>")!
Hello <em>Máté</em>!
We can deliberately request HTML encoded or raw HTML output with the HTML helper class:
Hello @Html.Raw(ViewBag.Name)!
var name = "@JavaScriptEncoder.Default.Encode(ViewBag.Name)";
JavaScript context escaping:
<iframe onload="...">real-content.pdf</iframe>
Server
Attacker
Target
2. Send malicious code
E.g. a mail with a dangerous URL as an image
1. Login to site
3. Execute command as a logged in user
The script/link inherits the third party's identity and privilege, and executes an request
E.g. cookie, cached logon credentials, IP address, client-side SSL authentication, etc.
The server cannot distinguish this from a real request: All the necessary credentials and permissions are ok!
GET /deleteRecord?id=15
<a href="http://www.app.com/deleteRecord?id=13">Click here for the free iPhone app</a>!
CSRF attacks can be instrumented in different forms:
Users are performing actions which they are authorized to do and must be able to do!
Back button?
Sometimes therefore only session-duration tokens
Like session ID, but sent with every link and form submission
Potential weakness: leaking the token, esp. in GET requests
Possible solution:
Send the token in POST requests only
Only ever use POST requests for data modifying operations
Users should:
Problem: this is not very dependable or user-friendly
Controller:
public function newAction(Request $request)
{
$form = $this->createFormBuilder() // adds CSRF token
// ...
->getForm();
$form->handleRequest($request); // validates CSRF token
if ($form->isSubmitted() && $form->isValid()) {
// perform some action...
return $this->redirectToRoute('success_action');
}
return $this->render('form.html.twig', array(
'form' => $form->createView(),
));
}
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }} {# adds CSRF field #}
View
Most MVC frameworks nowadays support integrate CSRF protection for forms. E.g. integrated into Symfony's form builder:
Controller:
public function newAction(Request $request)
{
$token = $request->request->get('_csrf_token');
$csrf_token = new CsrfToken('form_name', $token);
if ($this->isCsrfTokenValid('token_id', $csrf_token )
{
// ... do something
}
}
<form action="..." method="post">
{# ... the normal fields #}
<input type="hidden" name="_csrf_token"
value="{{ csrf_token('token_id') }}"
>
<input type="submit">Submit</input>
</form>
View
Try no to subvert the integrated solution. If you must, it is your responsibility to handle CSRF protection:
Controller:
[HttpPost]
// only process POST request
[ValidateAntiForgeryToken]
// defence against XSRF
public IActionResult SomeAction(
Int32 id, SomeViewModel vm)
{
// ...
}
<form asp-action="Index" asp-route-id="@Model.Id">
@* Field for XSRF token will be inserted here *@
@* ... *@
</form>
View:
As another example, in ASP.NET Core MVC framework, forms are automatically extended with a CSRF token:
<form method="post" action="Something/Index/42">
<input name="__RequestVerificationToken"
type="hidden" value="{token}" />
@* ... *@
</form>
Can be disabled with the asp-antiforgery server-side processed HTML attribute.
An attack can use XSS to obtain the token needed to work around CSRF protection!
<!-- TODO: Fix security issue here -->
Error messages should include the following information:
But in terms of the user, not of the developer!
Cascading Style Sheets: Describe how to show web content
Especially vulnerable: Mobile phones (tapjacking)
Example:
$path="/var/users/profiles/" . $_GET["user"];
include $_REQUEST['filename’];
The attacker may extract data from the server:
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>
The attacker may probe the server's private network:
<!ENTITY xxe SYSTEM "https://192.168.1.1/private" >]>
The attacker may attempt a DOS attack by including an endless file:
<!ENTITY xxe SYSTEM "file:///dev/random" >]>
<a href="http://www.good.com/redirect.php?url=www.evil.com">Go to good.com</a>
Server
Attacker
Target
2. Send session ID to victim, e.g. in a URL
3. Log in
Using this session ID
1. Start new session
Receive a session ID
4. Use site
With shared session of the victim
Basic idea:
Prevention:
Basic idea and premises (often existing!):
Exploit:
Prevention: remove CAPTCHA solution from session
after verification
Ensuring integrity of session information sent from client
Ensuring integrity of session information sent from client
If you do some encryption, the data is probably quite important
A bit of encryption is worse than no encryption
False sense of security!
Password
Password
Password
Hash
No cryptography
Cryptographically
insecure hash function
In case of a database intrusion and password table leakage the information can be easily utilized:
Password
Hash
Hash 1..N times
Salt
User
CSPRNG
Salt
Store
Use strong hashing algorithms, like SHA-512.
<?xml version="1.0"?>
<!DOCTYPE lolz [ <!ENTITY lol "lol">
<!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]>
<lolz>&lol9;</lolz>
Ensure that the resources any web request may use are
limited in various ways
Using obsolote, vulnerably third-party libraries, because:
Prevention:
Kevin D. Mitnick: The Art of Deception