AppCheck: Web Security Seminar – June 2023

Introduction

My employer offered to send me to the 2023 AppCheck Web Security Seminar. I attended representing the software development side of Real Group, along with Lee (IT Systems Manager), who was representing the physical infrastructure side of the the company.

As such, this post will mostly cover the software side of the seminar.

Location

Chelsea Football Stadium. Thankfully Lee is quite the Chelsea fan so knew the route to the stadium by heart. This saved me having to work out an unfamiliar route through London (not my strong point, it’s taken me 3 years to nail the route to our Greenwich office).

Amenities

Constantly refreshed citrus infused water, coffee and an overwhelming variety of teas were on offer throughout the day. Breakfast, lunch and snacks were also provided.

Not expecting breakfast offerings, Lee and I picked up some sandwiches on the walk through Chelsea town.

Snacks round 1

Cookies galore, we re-joined the queue for seconds. Not sure if this was allowed, nobody seemed to notice. There is probably some symbolism here, we accepted all the cookies.

Lunch

The poshest kebab I’ve ever had. A buffet line where guests helped themselves to build a meal. There was a range of meat fillings and vegetarian options, salad and chips.

Snacks round 2

Chocolate brownies, seconds were had.

Talks

Introduction

A 1 hour introduction which was used to introduce the concepts used in the following talks. This was more of an introduction to what AppCheck can offer it’s customers, how it works, how it compares to a manual penetration test and a brief company history.

There was noting too ground breaking here, but it was a welcomed introduction which laid the foundation for what was to come.

Web security

This is the area which is “up my street”. Unfortunately it mainly covered SQL injection, XSS and CSRF, maybe I’m showing my age, but these are fields that should be well known by any developer and drummed in at an early stage in your career.

SQL injection

This is a method of accessing the applications database in ways not intended. In short, any input variables should be pre processed before applying them to an SQL query. If you are not already using them, I would highly recommend using prepared statements with every database call – doing so will remove the possibility of SQL injection.

<?php
$username = filter_input(INPUT_POST, 'username');

$sql = "SELECT db.id, db.username, db.first_name, db.last_name, db.email
        FROM users AS db
        WHERE db.username = '$username';";

$data = $conn->query($sql);

If the user was to enter the following as their username

jdoe' UNION SELECT u.id, u.username, u.first_name, u.password, u.email FROM users AS u--

The final query would be

$sql = "SELECT db.id, db.username, db.first_name, db.last_name, db.email
        FROM users AS db
        WHERE db.username = 'jdoe' UNION SELECT u.id, u.username, u.firstname, u.password, u.email FROM users AS u --';";

The attacker would now have access to every username, password and email combination in the database. Thankfully the resolution of this is fairly simple with prepared statements.

Vanilla PHP

<?php
$username = filter_input(INPUT_POST, 'username');

$sql = "SELECT db.id, db.username, db.firstname, db.last_name, db.email
        FROM users AS db
        WHERE db.username = ?;";
$sth = $dbc->prepare($sql);
$sth->execute([$username]);

$data = $sth->fetchAll();

WordPress

<?php
global $wpdb;
$username = filter_input(INPUT_POST, 'username');

$sql = "SELECT db.id, db.username, db.firstname, db.last_name, db.email
        FROM users AS db
        WHERE db.username = %s;";

$data = $wpdb->get_results(
    $wpdb->prepare(
        $sql, $username,
    )
);

Cross Site Scripting

Cross Site Scripting or XSS is a vulnerability that allows the user to run their own code on your site and similar to SQL injection, the resolution is to escape the users input.

For example, if your website has a has a comments section (ignoring the database store and retrieval).

<?php
$name = filter_input(INPUT_POST, 'name');
$message = filter_input(INPUT_POST, 'message');

echo $name.' - '.$message;

If the user was to enter a message containing the text below, then every person who visited the site and read your message would be presented with their session cookie. This alone is quite benign as the user seeing their cookie won’t compromise anything.

<script>alert(document.cookie);</script>

Imagine that the malicious user executed javascript that transferred the session cookie to someone else, or initiated running malicious code within your browser.

Thankfully the resolution for this is simple, escaping the users input is all that is required.

Vanilla PHP

<?php
$name = filter_input(INPUT_POST, 'name');
$message = filter_input(INPUT_POST, 'message');

echo htmlentities($name, ENT_QUOTES).' - '.htmlentities($message, ENT_QUOTES);

This would cause the malicious message to be parsed as below; which would render the original message to the page as plain text rather than executable code.

&lt;script&gt;alert(document.cookie);&lt;/script&gt;

WordPress

<?php
$name = filter_input(INPUT_POST, 'name');
$message = filter_input(INPUT_POST, 'message');

echo esc_attr($name).' - '.esc_attr($message);

Similar to htmlentities, the WordPress esc_attr() function will will encode certain characters to be rendered as plain text rather than executable code.

Cross Site Request Forgery

Cross Site Request Forgery or CSRF is a method of tricking another user to perform a certain privilege action. Say for example an insecure bank had a page to transfer money from one account to another without any CSRF tokens to verify the origin. If the user is logged in, they can transfer money by navigating to www.insecurebank.com/transfer, filling in a form which then processes the request at the following URL www.insecurebank.com/transfer?acc=12312312&sc=010101&amount=100.00

Anybody could place a link to the bank on their website

Is <a href="www.insecurebank.com/transfer?acc=12312312&sc=010101&amount=100.00">Insecure Bank</a> offline for you?

I visit this site, am a member of insecure bank and see this message on a forum, curious to see if the bank is offline and having issues I click the link.

If I’m already logged into my online banking, this will automatically transfer ¬£100.00 from my account to the poster of the malicious message.

Resolution of this is a little more complex, but still simple to rectify.

Vanilla PHP

<?php
$token = filter_input(INPUT_GET, 'token');
if (!empty($token) && $token == $_SESSION['token']) {
    $acc = filter_input(INPUT_GET, 'acc');
    $sc = filter_input(INPUT_GET, 'sc');
    $amount = filter_input(INPUT_GET, 'amount');

    // Do something to process the transfer
    
    die('Transfer complete');
} elseif (!empty($token) && $token != $_SESSION['token']) {
    die('CSRF detected');
}

$_SESSION['token'] = bin2hex(random_bytes(32));
echo '<form method="GET">';
    echo '<input type="hidden" name="token" value="'.$_SESSION['token'].'">';
    echo '<label for="acc">ACC No</label><input type="text" name="acc" id="acc">';
    echo '<label for="sc">Sort Code</label><input type="text" name="sc" id="sc">';
    echo '<label for="amount">Amount</label><input type="text" name="amount" id="amount">';
    echo '<input type="submit" value="Transfer">';
echo '</form>';

A random token is generated and added to the $_SESSION variable, this token is then passed in the form and must match in order to process the form action.

WordPress

<?php
$wpnonce = filter_input(INPUT_POST, '_wpnonce');
if (!empty($wpnonce) && $wpnonce == wp_create_nonce('transfer')) {
    $acc = filter_input(INPUT_GET, 'acc');
    $sc = filter_input(INPUT_GET, 'sc');
    $amount = filter_input(INPUT_GET, 'amount');

    // Do something to process the transfer

    die('Transfer complete');
} elseif (!empty($wpnonce)) {
    die('CSRF detected');
}

echo '<form method="GET">';
    echo '<input type="hidden" name="_wpnonce" value="'.wp_create_nonce('transfer').'">';
    echo '<label for="acc">ACC No</label><input type="text" name="acc" id="acc">'; 
    echo '<label for="sc">Sort Code</label><input type="text" name="sc" id="sc">'; 
    echo '<label for="amount">Amount</label><input type="text" name="amount" id="amount">'; 
    echo '<input type="submit" value="Transfer">'; 
echo '</form>';

The wp_create_nonce function in WordPress creates a nonce (Number Once) based on the current users session and the value passed into it. This string can be made of static text and variables, i.e. you could use have an interim page between the form and the actual transfer that acts to verify the user is happy to continue.

<?php
$acc = filter_input(INPUT_GET, 'acc');
$sc = filter_input(INPUT_GET, 'sc');
$amount = filter_input(INPUT_GT, 'amount');

echo '<form method="GET">';
    echo '<input type="hidden" name="_wpnonce" value="'.wp_create_nonce('transfer'.$acc.$sc.$amount).'">';
    echo '<input type="hidden" name="acc" value="'.esc_attr($acc).'">';
    echo '<input type="hidden" name="sc" value="'.esc_attr($sc).'">';
    echo '<input type="hidden" name="amount" value="'.esc_attr($amount).'">';
    echo '<input type="submit" value="Confirm">';
echo '</form>';

Summary

This talk covered what I would consider the basics of web security with the key points being:

  • Sanitise SQL, use prepared statements
  • Don’t trust user input, always sanitise before printing to the page
  • Important or elevated actions require CSRF protection

I may have provided some very basic examples on how these methods work, can be exploited and can be remedied. Examples are to be used as a starting point and offer areas of exploration and research for those unfamiliar with the methods.

Infrastructure security

This was Lee’s area of expertise, I don’t feel that I’m qualified to translate the content provided and discussed in this section.

Domain Hijacking

The Domain Hijacking talk was a cross between infrastructure and web security, parts of this stood out to me, mainly the examples of

  • Domain squatting
  • Sub domain squatting – Shopify example
  • AWS / Google Cloud squatting
  • AWS / Google Cloud “Authenticated users”

Domain squatting

This is a technique that is as old as domain registry. Domains are registered for a set period of time, the current “Owner”, or more accurately “Leaser”, has first dibs on renewing a current domain. If they forget to renew the domain, it becomes available for others to register.

This is less of a problem these days as most domain providers offer a grace period, they will park an expired domain for a month or two to give the current owner time to realise that their website is offline while still having first dibs on renewal / registration.

Sub domain squatting

The example used for sub domain squatting was that of a website that also had a shop, hosted on Shopify.

Conclusion

As much as it was a fun day out, I feel that this could have just as easily been a webinar. This would have had the same effect (other than the lunch, snacks and day out) but instead allowing me to complete all talks in 4 hours while at work instead of 13 hours door to door.

This would also have allowed more people to attend, and only attend the relevant sections.


Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.