Broken SAML

Purpose

I wanted to set up broken SAML so students could practice hacking into it. It took me several days because none of the instructions I used were clear enough.

I'm preserving my procedure because I may want to do it again, and it may even help others who want to do it right.

I wrote these instructions for Ubuntu 16.04 Server. I also did this successfully on Kali 2018.1 and Ubuntu 17.10 Server with the same procedure.

Overview

SAML uses two servers: a Service Provider which limits service depending on the identity of a user, and an Identity Provider that collects credentials from the user and issues a token that users can use to prove their identity.

Setting up SAML requires these tasks:

1. Installing Apache and PHP
2. Configuring DNS Records
3. Configuring Virtual Servers
4. Set Up Service Provider
5. Set Up Identity Provider
6. Making an App
7. Breaking Security

1. Installing Apache and PHP

I started with a default Digital Ocean Ubuntu 16.04 server and executed these commands as root:
sudo apt-get update
sudo apt-get install php libapache2-mod-php php-xml
sudo service apache2 restart
To test it, I made a file named /var/www/html/phpinfo.php with these contents:
<?php
phpinfo();
?>
In a Web browser, I went to: http://samlol.samsclass.info/phpinfo.php and saw the PHP info page, showing that everything was OK. It's a good practice to remove that page later, but I am making a deliberately insecure server for students to hack, so I didn't.

Note about HTTPS

All these servers should be using HTTPS. I didn't do that, because I want students to be able to easily sniff and examine traffic to these boxes.

2. Configuring DNS Records

I configured DNS records this way: The three servers all go to the same IP address, but Apache will separate the requests because of the Virtual Server configuration below.

3. Configuring Virtual Servers

SAML is intended to use two different servers, but you can configure both the Identity Provider and the Service Provider on the same machine if you use Virtual Servers.

My two virtual servers will be named "idp.samsclass.info" and "sp.samsclass.info".

These commands make home folders for each server:

sudo mkdir -p /var/www/idp.samsclass.info/public_html
sudo mkdir -p /var/www/sp.samsclass.info/public_html
I used the nano text editor to make a home page for the IDP:
sudo nano /var/www/idp.samsclass.info/public_html/index.html
I put only the letters "IDP" in that file.

I used the nano text editor to make a home page for the SP:

sudo nano /var/www/sp.samsclass.info/public_html/index.html
It contains the letters "SP".

Next, I make the configuration file for the IDP:

sudo nano /etc/apache2/sites-available/idp.samsclass.info.conf
Here is the contents of that file:
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    ServerName idp.samsclass.info
    DocumentRoot /var/www/idp.samsclass.info/public_html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>

Next, I make the configuration file for the SP:

sudo nano /etc/apache2/sites-available/sp.samsclass.info.conf
Here is the contents of that file:
<VirtualHost *:80>
    ServerAdmin webmaster@localhost
    ServerName sp.samsclass.info
    DocumentRoot /var/www/sp.samsclass.info/public_html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
Executing these commands enables the sites:
sudo a2ensite idp.samsclass.info.conf
sudo a2ensite sp.samsclass.info.conf

sudo systemctl reload apache2
Now the two test pages are visible:

http://idp.samsclass.info/

http://sp.samsclass.info/

4. Set Up Service Provider

These commands download the simplesamlphp code:
cd /tmp
wget https://simplesamlphp.org/download?latest
These commands extract it into the SP's home folder and give it a handy directory name:
cd /var/www/sp.samsclass.info/public_html
sudo tar xvzf /tmp/download?latest
sudo mv simplesamlphp-1.15.4 simplesamlphp
I need to edit this Apache file to make the virtual directory "simplesaml", which simplesamlphp expects:
sudo nano /etc/apache2/sites-available/sp.samsclass.info.conf
Add this before the </VirtualHost> line:
    Alias /simplesaml /var/www/sp.samsclass.info/public_html/simplesamlphp/www
    <Directory "/var/www/sp.samsclass.info/public_html/simplesamlphp/www">
        Order deny,allow
        Allow from all
    </Directory>
Reload Apache:
sudo systemctl reload apache2   
Now this page loads in a browser, showing a simplesamphp installation page: http://sp.samsclass.info/simplesaml To make it functional, you must edit this file:
cd /var/www/sp.samsclass.info/public_html/simplesamlphp
sudo nano config/config.php
Change 'auth.adminpassword' and 'secretsalt' to non-default values.

5. Set Up Identity Provider

You need an second installation of simplesamlphp which is kept separate from the first one.

These commands extract it into the IDP's home folder:

cd /var/www/idp.samsclass.info/public_html
sudo tar xvzf /tmp/download?latest
sudo mv simplesamlphp-1.15.4 simplesamlphp
Again, you must edit this Apache file to make the virtual directory "simplesaml", which simplesamlphp expects:
sudo nano /etc/apache2/sites-available/idp.samsclass.info.conf
Add this before the
    Alias /simplesaml /var/www/idp.samsclass.info/public_html/simplesamlphp/www
    <Directory "/var/www/idp.samsclass.info/public_html/simplesamlphp/www">
        Order deny,allow
        Allow from all
    </Directory>
Reload Apache:
sudo systemctl reload apache2   
Now this page loads in a browser, showing a simplesamphp installation page: http://idp.samsclass.info/simplesaml To make it functional, you must edit this file:
cd /var/www/idp.samsclass.info/public_html/simplesamlphp
sudo nano config/config.php
As before, you must change 'auth.adminpassword' and 'secretsalt' to non-default values.

But this time, you must also set enable.saml20-idp to true -- to make this instance of simplesamlphp an Identity Provider. To enable authentication do this:

cd /var/www/idp.samsclass.info/public_html/simplesamlphp
sudo touch modules/exampleauth/enable
Now you need a data source for valid usernames and passwords. A database like MySql is the usual choice, but for my purposes a simple file containing hard-coded credentials is good enough, so I did this:
cd /var/www/idp.samsclass.info/public_html/simplesamlphp
sudo nano config/authsources.php
I added this inside the $config object. This makes two users:
'example-userpass' => array(
    'exampleauth:UserPass',
    'user1:pwd' => array(
        'uid' => array('user1'),
        'mail' => 'user1@test.com',
        'first_name' => 'User',
        'last_name' => 'One'
    ),
    'user2:pwd' => array(
        'uid' => array('user2'),
        'mail' => 'user2@test.com',
        'first_name' => 'User',
        'last_name' => 'Two'
    )
),
These commands make the RSA key needed for signatures:
cd /var/www/idp.samsclass.info/public_html/simplesamlphp
cd cert
sudo openssl req -newkey rsa:2048 -new -x509 -days 3652 -nodes -out server.crt -keyout server.pem
Now you need to edit this file to tell the Identity Provider where your code-signing keys are:
cd /var/www/idp.samsclass.info/public_html/simplesamlphp
sudo nano metadata/saml2-idp-hosted.php
Put this text into that file:
$metadata['__DYNAMIC:1__'] = array(
    /*
     * The hostname of the server (VHOST) that will use this SAML entity.
     *
     * Can be '__DEFAULT__', to use this entry by default.
     */
    'host' => '__DEFAULT__',

    /* X.509 key and certificate. Relative to the cert directory. */
    'privatekey' => 'server.pem',
    'certificate' => 'server.crt',

    /*
     * Authentication source to use. Must be one that is configured in
     * 'config/authsources.php'.
     */
    'auth' => 'example-userpass',

    /* Uncomment the following to use the uri NameFormat on attributes. */
    /*
    'attributes.NameFormat' => 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri',
    'authproc' => array(
        // Convert LDAP names to oids.
        100 => array('class' => 'core:AttributeMap', 'name2oid'),
    ),
    */
);
You need to edit this file to tell the Identity Provider where your passwords are:
cd /var/www/sp.samsclass.info/public_html/simplesamlphp
sudo nano config/authsources.php
Edit the default-sp section to look like this. The only three lines to add are the ones beginning with 'entityID', 'idp', and 'ssoPortalUrl'. Delete or comment out the existing lines beginning with 'entityID' and 'idp'.
    'default-sp' => array(
        'saml:SP',

    'entityID'    => 'http://idp.samsclass.info',
    'idp'         => 'http://idp.samsclass.info/simplesaml/saml2/idp/metadata.php',
    'ssoPortalUrl'=> 'http://idp.samsclass.info/simplesaml/saml2/idp/SSOService.php',

        // The URL to the discovery service.
        // Can be NULL/unset, in which case a builtin discovery service will be used.
        'discoURL' => null,
        
        ...
In a browser, go to http://idp.samsclass.info/simplesaml

Click the Federation Tab.

Log in as admin with the password you specified earlier.

You should see a SAML 2.0 IdP Metadata line, click on the [show metadata] link below it. The metadata should appear!

Troubleshooting

If the metadata will not load, look in the syslog with
tail -f /var/log/syslog
If you see "SimpleSAML_Error_Exception: Error 1 - Class 'DOMDocument' not found", you probably don't have the php-xml library installed, or there is a conflict with two versions of PHP installed at once. I found that this can be fixed by completely removing all old PHP files and then installing PHP 7.0 or 7.2, including php-xml or php7.2-xml.

Syslog may show this error: "SimpleSAML_Error_Exception: Error 2 - session_set_cookie_params(): Cannot change session cookie parameters when session is active". If so, just ignore it. It seems harmless.

You can test the metadata script from the command line with these commands:

cd /var/www/idp.example.com/public_html/simplesamlphp
cd www/saml2/idp
php metadata.php
The reply contains a lot of errors like this: "SimpleSAML_Error_Exception: Error 8 - Undefined index: REQUEST_URI"

I spent a long time trying to fix that, but finally realized that it doesn't matter. Just ignore it. The server works fine anyway.

Scroll down to the "SimpleSAMLphp flat file format" and paste it in the file metadata/saml20-idp-remote.php of your SP.
cd /var/www/sp.samsclass.info/public_html/simplesamlphp
sudo cp metadata/saml20-idp-remote.php metadata/saml20-idp-remote.php.bak
sudo nano metadata/saml20-idp-remote.php
Append the $metadata at the end, like this:
$metadata['http://idp.samsclass.info/simplesaml/saml2/idp/metadata.php'] = array (
  'metadata-set' => 'saml20-idp-remote',
  'entityid' => 'http://idp.samsclass.info/simplesaml/saml2/idp/metadata.php',
  'SingleSignOnService' => 
  array (
    0 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
      'Location' => 'http://idp.samsclass.info/simplesaml/saml2/idp/SSOService.php',
    ),
  ),
  'SingleLogoutService' => 
  array (
    0 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
      'Location' => 'http://idp.samsclass.info/simplesaml/saml2/idp/SingleLogoutService.php',
    ),
  ),
  'certData' => 'MIIDXTCCAkWgAwIBAgIJAJK3x7G6tuOEMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMTgwNDAxMTYyNDE4WhcNMjgwMzMxMTYyNDE4WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyf4AHwhNDxXD4Mi/GkuV48ZJKdwK9xcXty0kM3vj2pye//RGaYvcl7jGZyoP0Nfl01dRTHf5TDxY/192Zxlmn9LupoiuuoenkA0EHcsYNFe8aoJ/CIyXGZbTDgK+ZbR2g264h6x3FrqTbwN1pV7en/KwQJ226T9fGDzWraGopqliSINTGMk2cXxEEo0+W6TkxmGktL7h+It0vsxZX7T0dK/nXk5ClKb/Cr+gYQ5qRWOuNyjDYNPIWC1WMdwSEJxN8WB0TNAlzZXxZA35DMsOzLaFRiNlqF1iv7SWaMU3AoBPf9703fTheFVD379qddrukehmirhBHs2s0BgByksY5wIDAQABo1AwTjAdBgNVHQ4EFgQUT8J76xFu9666xXwVgnxs9bhk3gUwHwYDVR0jBBgwFoAUT8J76xFu9666xXwVgnxs9bhk3gUwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAaKPVRNbjYZEPfbH51YeGEGV2jcaMrGvVEZSuWLM7t8aoYBzEPC9fCi2iC4BWgUKzdJPSlwvd5BCwkqciCoOYTncxU7oi+GwbpvjNSGjf8OW259OoI0lUItTCKjJmjj07i+syo4b63o51pGP2lSCQQtoPrG8ObRX7lKXFVKdwkXxz2p4TcRP7+yUpqd9b3vZTW3ApdNGRDHMLVBZlI78i3lCf5y1mF3xLkT7nhAV8mVjfpg+uFFJjkbO7vRj8IxM+AsVEmNWaUwsD8o6DzA/U0xNQMRndhyupjLTuN5ACoXnfGSoerqb5Us3euGJwDrHqoQbTX60yxir0t6yMUGFFFw==',
  'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient',
);
You also need to edit this file:
cd /var/www/idp.samsclass.info/public_html/simplesamlphp
sudo cp metadata/saml20-sp-remote.php metadata/saml20-sp-remote.php.bak
sudo nano metadata/saml20-sp-remote.php
In a browser, open http://sp.samsclass.info/simplesaml

Click the Federation tab, and login as admin with the password you chose previously.

Click on the [show metadata] link for your default-sp.

Scroll down, copy the content of the "SimpleSAMLphp flat file format" box and paste it in the IdP metadata/saml20-sp-remote.php file.

Replace the metadata with this:

$metadata['http://idp.samsclass.info'] = array (
  'SingleLogoutService' => 
  array (
    0 => 
    array (
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
      'Location' => 'http://sp.samsclass.info/simplesaml/module.php/saml/sp/saml2-logout.php/default-sp',
    ),
  ),
  'AssertionConsumerService' => 
  array (
    0 => 
    array (
      'index' => 0,
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
      'Location' => 'http://sp.samsclass.info/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
    ),
    1 => 
    array (
      'index' => 1,
      'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:browser-post',
      'Location' => 'http://sp.samsclass.info/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp',
    ),
    2 => 
    array (
      'index' => 2,
      'Binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact',
      'Location' => 'http://sp.samsclass.info/simplesaml/module.php/saml/sp/saml2-acs.php/default-sp',
    ),
    3 => 
    array (
      'index' => 3,
      'Binding' => 'urn:oasis:names:tc:SAML:1.0:profiles:artifact-01',
      'Location' => 'http://sp.samsclass.info/simplesaml/module.php/saml/sp/saml1-acs.php/default-sp/artifact',
    ),
  ),
);
To test it, in a browser, go to http://sp.samsclass.info/simplesaml You should be logged in as administrator

Click on the authentication tab. Click on the link "Test configured authentication sources" and click on "default-sp"

Log in as user1 pwd

You should see a page showing your User ID, Mail, etc.

6. Making an App

I put my app here:
/var/www/sp.samsclass.info/public_html/simplesamlphp/www/win.php
Here's its code:
<html>
<head>
<title>
SAMLOL Challenge
</title>
</head>
<body bgcolor="#cccccc">
<h1 align="center">
SAMLOL Challenge
</h1>

<blockquote><table border=5 cellpadding=10 align="center"><tr><td>
<?php

require_once('../lib/_autoload.php');
$as = new \SimpleSAML\Auth\Simple('default-sp');

$attrs = $as->getAttributes();

if (!isset($attrs['uid'][0])) {
    print "<h2 align='center'>You are not logged in!</h2>";
    print "<img src='sleep2.png'><p>";

    print "<p align='center'>You can use these accounts: <b>user1</b> <b>pwd</b> and <b>user2</b> <b>pwd</b></p>";

    $url = $as->getLoginURL();
    print "<b><p  align='center'><a href='" . htmlspecialchars($url) . "'>Login</a></b></p>";

    print "<p align='center'>The 1337 kids are on the <a href='http://samlol.samsclass.info/tmp/samlol-winners.htm'><b>WINNERS PAGE</b></a>.</p>";

    print "<p align='center'><a href='https://samsclass.info/129S/proj/p16saml.htm'><b>Explanation and Instructions</b></a>.</p>";
         
} else {

    $name = $attrs['uid'][0];
    if ($name != 'admin') {
        print "<h1 align='center'>UNAUTHORIZED</h1>";
        print "<h2 align='center'>You are logged in as <i>" . htmlspecialchars($name) . "</i></h2>";
        print "<h2 align='center'>You must be <i>admin</i> to win!</h2>";
        print "<img src='hiss.png'><p>";

        $url = $as->getLogoutURL();

        print "<p  align='center'><b><a href='" . htmlspecialchars($url) . "'>Logout</a></b></p>";

        // $url = ".../simplesaml/module.php/core/as_logout.php?AuthId=default-sp&ReturnTo=win.php";
        // print('<a href="' . htmlspecialchars($url) . '">Logout</a>');
    } else {
        print "<h1 align='center'>YOU WIN</h1>";
        print "<h2 align='center'>You are logged in as <i>" . htmlspecialchars($name) . "</i></h2>";
        print "<img src='happy.jpg'><p>";

        print "Use the form below to put your name on the <a href='http://samlol.samsclass.info/tmp/samlol-winners.htm'>";
        print "<b>WINNERS PAGE</b></a>.";

        print "<blockquote><form action='win-board.php' method='post'>";
        print "<table cellpadding=5 border=10><tr><td>";

        print "<table cellpadding=5 align='center'>";
        print "<tr><td><big><b>Your Name:</b></big></td>";
        print "  <td><textarea name='u' rows='1' cols='25'></textarea></td></tr>";
        print "<tr><td colspan=2 align='center'><big><b>";
        print "<input type='submit' value='SUBMIT'></td></tr>";
        print "</table>";

        print "</td></tr></table>";
        print "</form>";
        print "</blockquote>";
        
        $url = $as->getLogoutURL();
        print "<p  align='center'><b><a href='" . htmlspecialchars($url) . "'>Logout</a></b></p>";
    }
}
?>

</td></tr></table>
</blockquote>

</body>
</html>


7. Breaking Security

I modified three files:
/var/www/sp.samsclass.info/public_html/simplesamlphp/modules/saml/www/sp/saml1-acs.php
/var/www/sp.samsclass.info/public_html/simplesamlphp/modules/saml/www/sp/saml2-acs.php
/var/www/sp.samsclass.info/public_html/simplesamlphp/modules/saml/lib/Message.php

Modification 1

I modified this file:
/var/www/sp.samsclass.info/public_html/simplesamlphp/modules/saml/www/sp/saml1-acs.php
Around line 63, I changed it to this, to make it accept responses with no signatures:
} elseif (array_key_exists('SAMLResponse', $_REQUEST)) {
        $responseXML = $_REQUEST['SAMLResponse'];
        $responseXML = base64_decode($responseXML);
        $isValidated = TRUE; /* CHANGED BY SAM TO BREAK SECURITY 3-31-18 Must check signature on response. */
        /* $isValidated = FALSE;  Must check signature on response. */

Modification 2

I modified this file:
/var/www/sp.samsclass.info/public_html/simplesamlphp/modules/saml/www/sp/saml2-acs.php
It had the number "60" in line 158, to make it accept time discrepancies up to 60 seconds. I changed that to 6000, so it now looks like this:
        $notOnOrAfter = $assertion->getNotOnOrAfter();
        if ($notOnOrAfter === null) {
            $notOnOrAfter = time() + 24 * 60 * 60;
        } else {
            $notOnOrAfter += 6000; // CHANGED FROM 60 to 6000 BY SAM TO BREAK SECURITY we allow 60 seconds clock skew, so add it here also
        }

        $store->set('saml.AssertionReceived', $aID, true, $notOnOrAfter);
    }

Modification 3

I modified this file:
/var/www/sp.samsclass.info/public_html/simplesamlphp/modules/saml/lib/Message.php
Around line 613, I changed it to this, to make it accept responses with no signatures, and to allow time discrepancies up to 6000 seconds:
/* SIGNATURE CHECK COMMENTED OUT BY SAM TO BREAK SECURITY 

        if (!self::checkSign($idpMetadata, $assertion)) {
            if (!$responseSigned) {
                throw new SimpleSAML_Error_Exception('Neither the assertion nor the response was signed.');
            }
        } // at least one valid signature found

*/

        $currentURL = \SimpleSAML\Utils\HTTP::getSelfURLNoQuery();

        // check various properties of the assertion
        $notBefore = $assertion->getNotBefore();

/* 60 changed to 6000 by SAM to BREAK SECURITY */

        if ($notBefore !== null && $notBefore > time() + 6000) {
            throw new SimpleSAML_Error_Exception(
                'Received an assertion that is valid in the future. Check clock synchronization on IdP and SP.'
            );
        }
That's it. This produces the broken application here:

http://samlol.samsclass.info

Sources

How To Set Up Apache Virtual Hosts on Ubuntu 14.04 LTS
Installing SimpleSAMLphp and use it as SP and IdP (for development env. only)
SimpleSAMLphp Implementing custom username/password authentication
SIMPLESAMLPHP SAML 2.0 SSO SINGLE SIGN ON: SETTING UP A LOCAL SP & IDP
SimpleSAMLphp SP API reference

Posted 4-1-18 1:29 pm
Minor fix 4-4-18 7 am