What is an AppSec Program?

What is an AppSec Program?

Matt Konda No Comments
  Application Security Strategy

Most companies that we work with are building software. That’s not a surprise because that’s our niche. Yet a surprising number of those companies don’t know about application security programs. Even companies with sophisticated security teams often struggle with application security and don’t take a programmatic approach to it. Why? Because it is really hard and requires knowledge of how application development and SDLC’s work. In this post, I’ll talk about some of the high level parts of successful AppSec Programs we’ve seen.

Inventory

One of the first things we need to effectively run an AppSec program, is an inventory of what applications we have. At its simplest, this could be a spreadsheet. Ideally, it is automated or dynamically updated. Once we have an inventory, we can map activities and costs to that. Without an inventory, we can’t do anything concrete with the program.

Tiers

Many companies we work with have hundreds, thousands or even tens of thousands of applications, services, repositories, etc. Even for those with just 10, in order to help us focus, we need to decide which applications we really care about.

We usually introduce the concept of Tiers here, Tier 1, Tier 2 and Tier 3. Then we work with a client to decide what criteria make an application a Tier 1 application.

Activities

There are dozens of things we can do to improve the security of our applications. We can use tools like SAST, DAST, IAST, WAF, and on and on and on. Those may be important, but in many cases, we can be pretty sure that the inventory will dictate what is realistic. If we have 10,000 applications, we can’t just point a SAST tool at it and expect to to work in any kind of time frame.

It can be dangerous to confuse a specific activity with a program. As a specific example, some tools (eg. SAST or IAST) believe that they are your dashboard for application security. That negates all the other parts of the program that you need (eg. training, security requirements, etc.).

A key part of building an AppSec program is working with stakeholders to know what activities are going to be fundable. Another key part is to work on cost effective activities while building to more expensive ones. A final key point about program activities is that the best fit activities are different at every company we work with. At the end of the post, we’ll talk about things that we do as part of AppSec programs, but it is not the same and needs to fit the development culture and SLDC.

Budget

Building a program without thinking about budget won’t work. Once we know about activities, and which will be applied to which applications we can start to know how much work we have. That means we can estimate budgets.

We need to get beyond the idea of 10% incremental increases in AppSec budget. Having an inventory and a proactive idea about what we need to do for which application gives us a constructive way to build a budget. We all know that if we just say we want security but we don’t fund it, we won’t get it.

Complexity

Something that is fundamentally tricky about AppSec is that it is complex. As we’ve written in the past, there are some things that tools just won’t find.

By embracing a customized program that values training and security requirements we invest in our people and their unique ability to understand potential security needs. If our program doesn’t emphasize that human factor, we may be strong in some ways but we won’t be in others.

Why A Program?

A program is like a plan of plans. Maybe each software project has a mini plan for application security. The program is the assembled set of all project plans for doing software security.

The program can develop to include dates, costs, and metrics. The whole idea is that we want to know if we are succeeding or failing – then we want to be able to improve in targeted ways.

References

Example AppSec Modules

  • Baseline security requirements
  • Automating inventory
  • Automating dependency checks
  • Automating static analysis (SAST)
  • Automating dynamic scanning (DAST)
  • Automating configuration checks (AWS)
  • Security requirements
  • Security unit tests
  • Encryption
  • Single Sign On
  • Security Logging (Signal)
  • Security Audit Logs (Audit)
  • WIKI Presence
  • Threat Model  
  • Sprint Security Requirements
  • Sprint Security Checklists
  • Sprint Security Unit Testing
  •  Training
  • Code Review
  • App Pen Test
  • OWASP ASVS
  • OWASP OpenSAMM
  • Cloud Auditing
  • Log Review
  • Tool Selection or Review
  • Security Intelligence
  • Data Classification
  • Deep Dive on Credit Card Flow
  • Deep Dive on EncryptionPassword Flow and Handling Review
  • Honeypots
  • Honey Emails
  • Maturity Model Integration

Implementing Authorization Properly

Matt Konda No Comments
  Application Security Engineering

Introduction

Almost every time we do a penetration test or code review, we find problems with authorization.  Sometimes we call these horizontal or vertical privilege escalation.  Sometimes we call it instance based restriction gaps or function based restriction gaps.  Ultimately, many applications fail to implement clear restrictions on who can do what.  This post attempts to revisit these types of findings, explain a bit about how we test for them and talk about some standard ways of addressing them.

Dispelling A Dangerous Assumption

Sometimes we hear developers say that a particular issue is not important because a user has to be authenticated (usually by presenting a user and password) before they could ever cause the issues we are showing.  Yet, in many systems, it is trivially easy to enroll with a throw away account and learn about how the system works.  That means that I can both get an account easily and I can generally hide who I really am while I’m testing.  So the truth is that in many many systems, it is perfectly possible to experiment with authorization as an anonymous but authenticated user.

In cases where the authentication process is more rigorous, or in a business to business setting, the risk may be reduced but it is probably not eliminated.  We certainly see people trying to guess and misuse API tokens.

Instance Based Authorization Failures (Horizontal Privilege Escalation)

The easiest way to think about instance based authorization (authz) failures, is to use an example.  Consider the case where I am working with a timesheet.  The system presents me with a form for my timesheet.  As I fill out the form, the javascript front end is watching for changes and saving them in the background as I go.  Here is an abbreviated example of a background POST that saves the current work in progress on a timesheet:

POST /entry/save HTTP/1.1
X-CSRF-Token: ABC--
Cookie: _time_sess=abc
day015_project18402963_task7138879=2.00&changed_fields=day015_project18402963_task7138879&of_user=307476&approval_period_start=014&approval_period_start_year=2019&x_requested_with=XMLHttpRequest&authenticity_token=S9zI

What is interesting here is that as a legitimate user, I can capture any number of realistic requests like this.  Once I have a few, I can examine them and replay them quite easily within a valid session.  In this case, see the red and bold user id number (307476).  That appears to be tied to my logged in user.  It is quite likely that other numbers would trigger this request being applied to other users.  The question is, is there anything in the back end logic to prevent a request for user 307477 from successfully updating that user’s data from my session?

The simplest possible prevention of this type of problem is to tie each instance of a timesheet to its owning user and to only allow users to update their own timesheets.  This might look like the following in CanCan, which is a handy Authz library used with Ruby on Rails that we like for illustration because it is direct:

class TimesheetController < ApplicationController
  load_and_authorize_resource

  def show
    # @timesheet is already loaded and authorized
  end
end

Typically this depends on the Timesheet object having a column tying it to a user that is the owner.  The magic here is that CanCan does all of the work to load and check the owner automatically for any kind of model object.

This might look like this in Java with Apache Shiro:

Subject currentUser = SecurityUtils.getSubject();
currentUser.checkPermission("timesheet:2345:approve"); 
approveTimesheet();

We also see this custom coded in many clients.

One way or another, we need to make sure to control access to objects at an instance level.

Function Based Authorization Failures (Vertical Privilege Escalation)

Typically function based access control is implemented with role mappings.  Some examples of vertical privilege escalation is if a user can call a function they shouldn’t be allowed to call.  For example, a team member Natalie is not a manager so she should not be able to approve timesheets.  We would never expect the following POST to start from her browser, because we wouldn’t expect the button that triggers the post from even showing.

POST /approve/finalize?return_to=…I wonder if this is open redirect but that is another topic…  HTTP/1.1
Cookie: _timesheet_sess=ABC
Upgrade-Insecure-Requests: 1

authenticity_token=ABC&user_id=1770921&period_start=365&period_start_year=2018&from_page=0&commit=Approve+Timesheet

Since the Approve button is only visible to people in the admin or manager role, developers wouldn’t expect this post to ever happen so they might not write code to prevent it.

Note that the user id is also in this payload.  It is easy enough to imagine taking a request like this and replaying it for every id in the system for every period.  Burp Intruder would make that an easy exercise that requires no programming.

Something to keep in mind is that Single Page Apps (SPA’s) like those written in Angular, typically ship a Controller to the client which can be easily explored to identify potential endpoints that might not otherwise be obvious.  This only raises the importance of implementing comprehensive authorization on the server.

The Spring Security annotations illustrate how roles are often enforced:

@PreAuthorize("hasRole('ROLE_MANAGER')")
public void approve(Timesheet timesheet);

This might look like this in Java with Apache Shiro:

Subject currentUser = SecurityUtils.getSubject();
currentUser.checkPermission("timesheet:approve"); 
approveTimesheet();

You get the idea.

Note that we most commonly have to do both function and instance based approval.  Meaning, I can approve some timesheets but only those from people on my team – not across the company or all of the tenants in the solution.  That means that the real logic needs to check both:

  • The POST /approve/finalize …
  • The user_id=1770921

I must be a user that has the role required to approve timesheets AND I should be specifically allowed to approve this user’s timesheet.

Defining Intended Authorization

As is often the case with software, a lot of times the authorization gaps we find have to do with a lack of communication between a product manager (stakeholder) and the engineering team.  The requirements said that Matt should be able to see and submit his timesheet.  The requirements did not say that Matt should NOT be able to see Joe’s timesheet or delete Brian’s timesheet.  Since the requirements were never captured, the engineers may not have stopped to think about what could go wrong.  Sometimes making these requirements work is easy, sometimes it is hard.

Once we’ve defined the intended authorization logic, we can also test it.  This is a place where programmatic unit and functional testing can be extremely effective.  We always recommend writing security unit tests and incorporating things such as horizontal and vertical access attempts (and blocking) in the tests.

Bonus Points

Whenever authorization fails, we need to know.  Most systems don’t make it easy to call something where you are not authorized.  If you click a button to delete something as an admin, we presume that you saw the button.  If as described above, an unauthorized user sends a request that suggests they clicked the button and we detect and block that, we should also LOG that and escalate because there is almost certainly something nefarious going on.  Even in a horizontal access case, if I try to access a timesheet for another user with an ID I shouldn’t have access to and you block it, the event should be logged in such a way that security will see it.

Another area that makes a big difference when it comes to access control is simplifying management of user roles.  In practice, this might translate to a single source of the truth like Active Directory tied to a SSO solution that transmits group memberships from AD and end solutions that translate those AD group memberships into local group memberships or roles.  This way, by removing a user from a group in Active Directory, I can remove them from the admin role in the Timesheet system.

Advanced Paradigms

A lot of time instance based access control boils down to keeping a more detailed authorization database (or store of some kind) that understands user roles and groups and therefore object ownership.  Function based access control can be implemented based purely on roles and coded with annotations or fairly broad controller level directives in most languages.  Sometimes what we really want though is a more contextual understanding of the user.  Attribute based access control extends on the idea of RBAC and adds the idea that a “yes” or “no” decision on whether a user can do something may incorporate other attributes – things like geolocation, time of day, effective role, recent activity, etc.

We also see changes happening in terms of roles being assigned for a limited time.  Systems like AWS STS or Hashicorp Vault can help implement session based role assignment.  In this paradigm, a user is given the right to assume a role.  The user itself does not have any privileges directly assigned.  Rather, when they need them they ask and assuming they have been cleared to ask for that access, they are granted specific access to what they requested for a limited time.  Since the user doesn’t have any rights directly and the request for any session is audited, this reduces exposure due to lost credentials and makes any role usage visible at granular level.

While we’re talking, we should also think about how we implement authorization on our computing platforms.  Kubernetes offers RBAC based controls to define who can do what in a given cluster, pod, etc.  As defining the environment moves toward being a developer task, we need to keep these authorization scenarios top of mind.

An area we haven’t seen a good consistent solution is in service to service authorization.  Usually within a given set of boundaries, microservices authenticate (or not!) and then return or don’t.  We believe there is room to grow frameworks for controlling what data can be requested by which microservices and this is an area we’re putting in some research work.

Conclusion

As developers, we always need to check that the logged in user has the right to perform the action requested against the instance(s) requested.  That implies both checking the function and the instance.

References

 

JASP Check Deep Dive: Redshift

Matt Konda No Comments
  Cloud Security JASP

Introduction

Redshift is Amazon’s data warehousing solution.  Here’s how they describe it at:  https://aws.amazon.com/redshift/.

Redshift delivers ten times faster performance than other data warehouses by using machine learning, massively parallel query execution, and columnar storage on high-performance disk. You can setup and deploy a new data warehouse in minutes, and run queries across petabytes of data in your Redshift data warehouse, and exabytes of data in your data lake built on Amazon S3.

Obviously, anywhere you have lots of data is a place where security matters.  So let’s talk about what JASP will check about at Redshift environment.  Before we do that we should make sure to point out that with Redshift, we’re usually talking about clusters and many of the parameters or settings for those are managed by parameter groups.

Encryption

So … if you have lots of data, especially if you think there might be anything sensitive in it, you should probably think about encrypting that data.  Redshift makes that relatively easy but often people don’t always do it.  JASP will check this to make sure your Redshift data is encrypted.  Redshift works with clusters.  JASP checks each cluster to see if it has been configured with encryption – which is literally a radio button in the cluster configuration.

For most organizations, using KMS is totally reasonable.  You may want to have different keys for different environments or purposes.

Public

Another thing we check with JASP is whether Redshift is accessible publicly.  We would never expect public access to a Redshift cluster to be intended.  In practice, this looks like a cluster with a VPC and Security Group with open ports to the outside world.  It is easy to check via the API as well.

aws redshift describe-clusters

Upgrades

Redshift also has a setting that allows it to be updated.  This has obvious risk, in the case that there is some kind of change that breaks something.  It also has an obvious upside, which is that if there are any security issues that indicate an update is needed, they will be applied automatically.  JASP checks this setting as well.

SSL

We can check that SSL is required when connecting to Redshift by checking each parameter group and ensuring that they require SSL.  Generally speaking, we would expect connecting to access sensitive data to be over an SSL/TLS connection.  To get more information about parameter groups from the CLI, we can do this:

aws redshift describe-cluster-parameter-groups

Activity Logging

Redshift makes it easy to log user connections, changes to users and queries run.  Having this logging on provides an audit trail and is strongly indicated for any data stores with sensitive or regulated data.  JASP checks this on each parameter group.

Conclusion

AWS Redshift has a pretty basic profile in terms of security.  Without diving deeper into what data is present, we can still make some initial observations and very general security recommendations.

References

  • https://aws.amazon.com/redshift/
  • https://docs.aws.amazon.com/redshift/latest/APIReference/Welcome.html
  • https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-db-encryption.html
  • https://docs.aws.amazon.com/redshift/latest/mgmt/changing-cluster-encryption.html
  • https://docs.aws.amazon.com/redshift/latest/mgmt/getting-started-cluster-in-vpc.html
  • https://docs.aws.amazon.com/redshift/latest/mgmt/working-with-parameter-groups.html
  • https://docs.aws.amazon.com/redshift/latest/mgmt/managing-clusters-console.html#rs-mgmt-set-maintenance-track
  • https://docs.aws.amazon.com/cli/latest/reference/redshift/describe-clusters.html
  • https://docs.aws.amazon.com/redshift/latest/mgmt/connecting-ssl-support.html
  • https://docs.aws.amazon.com/redshift/latest/mgmt/db-auditing.html

Don’t rely on X-XSS-Protection to protect you from XSS

Brian Fore No Comments
  Application Security Engineering

Introduction

The X-XSS-Protection header only helps protect against certain reflected XSS attacks. It does nothing for stored XSS attacks. Don’t rely on it to protect your site from XSS!

What it can do: Block reflected XSS attacks

Reflected XSS occurs when a malicious query parameter in a page’s URL is rendered unsanitized on the page.

The XSS protection against this is not implemented uniformly across browsers (and it’s not supported at all by Firefox). But the basic idea is that the browser searches through the html looking for a script tag that exactly matches the script tag in the URL. If it finds a match, then the page is prevented from loading.

As an example, suppose our page takes the ‘user’ query parameter from the URL and displays it in a welcome message:

We’ll turn off Chrome’s default XSS auditor by including the following header in our server responses (this is not something you should normally do):

X-XSS-Protection: 0

Now we’ll replace ‘Mario’ with a script tag and see what happens:

With the X-XSS-Protection turned off, the script runs and an alert box appears.

Now we’ll turn the X-XSS-Protection back on by including the following header in our server responses (this is the usual setting we recommend):

X-XSS-Protection: 1; mode=block

We’re blocked when we attempt to reload the page:

We see the following message in the Chrome dev console:

The XSS Auditor blocked access to ‘http://localhost:8080/todos?user=%3Cscript%3Ealert(1)%3C/script%3E’ because the source code of a script was found within the request. The server sent an ‘X-XSS-Protection’ header requesting this behavior.

Here we have an example of the X-XSS-Protection in action. It has signaled to the browser to block the loading of the page, because

<script>alert(1)</script>

appears both in the URL and within the page’s html.

We generally recommend setting X-XSS-Protection: 1; mode=block, which (as we saw above) prevents the loading of the page if a reflected script is detected. An alternative is to set X-XSS-Protection: 1, which signals the browser to sanitize the reflected script and then continue to load the page.

What it can’t do: Block stored XSS attacks

Stored XSS occurs when a user submits malicious input to the website, the website stores this input, and then redisplays it, unsanitized, on one or more pages on the site.

As an example, suppose the todos on our site are displayed unsanitized, and the user submits a new todo with an onmouseover action:

When the user clicks Submit, the todo is stored server-side and the page refreshes with the updated list of todos from the server. When we hover over the malicious todo we see the alert popup:

The X-XSS-Protection does nothing against this sort of attack.

Summary

Generally speaking you should include the X-XSS-Protection header in your server responses:

X-XSS-Protection: 1; mode=block

But realize this only helps protect against certain reflected XSS attacks, and it’s not implemented uniformly across browsers. It will not protect against stored XSS attacks.

So how can you protect your site from stored XSS attacks?

  • Have a strong Content-Security-Policy.
  • Sanitize (or reject) malicious user input before it is stored server-side.
  • Always sanitize user input before it is displayed client-side. (Modern frameworks will usually take care of this for you, assuming you follow their best practices.)
  • Don’t assume 3rd party libraries (markdown editors, chart creators, etc.) are properly sanitizing all possible user input.

References

  1. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
  2. https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
  3. https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

JASP Check Deep Dive: S3

Matt Konda No Comments
  Cloud Security JASP

It is very common to find Amazon S3 buckets misconfigured. 

We found one in a pen test this week.  We find them frequently.  The most common things we see with S3 buckets is that people leave them open to the world and don’t encrypt them.  The one we found this week also let us delete and write files.

Something cool about using a tool like JASP (https://app.jasp.cloud) is that it will not only detect the kinds of settings we’re about to go deeper on, but it will also check them every day and alert you if something changes.  Finally, you should be able to go look at reports to determine when the bucket first showed up with that config (though ideally you could get that from CloudTrail too).

Why encrypt S3 drives?

Since we’re in a shared file system in AWS, even though we expect AWS to prevent anyone from ever being able to read raw disk attached to any system we run in our account, because there could be shared host systems or infrastructure, we need to take extra precautions to make sure the data we write isn’t mistakenly available to another tenant.  This is also why we advise encrypting any other data storage as well.  Fundamentally, if a rogue user were able to identify a problem and break out of their guest instance to read raw disk, I don’t want them to know what I have.  If I encrypt the disk, they shouldn’t be able to.

Thinking about permissions for S3 drives

Sometimes S3 drives are used to host files like images, videos or web content.  In that case, you need the files to be readable to be able to use the service the way you want.  In that event, we would recommend double checking that the directory is not listable.  In general, we don’t want directories to be listable.  We would also recommend using different buckets if you intend to have some files be readable and others not be.  Finally, and this sounds obvious when we say it like this, but if the intent is for people to be able to read the files, don’t let them write or delete them!

Other times we have S3 buckets that are more like file sharing drives.  We want a limited group of people to be able to read those buckets.  Of course, we also want a limited number of people to be able to write or delete in those buckets as well.

Logging

A couple of things related to S3 and logging.  First, your cloudtrail logs that get stored in S3 should not be publicly readable.  Second, access to any non web files should probably have access logging going to cloudtrail.  That will come in handy if you ever need to know who did read that file.

Conclusion

These are just some examples of things that JASP (https://app.jasp.cloud) can identify for you related to S3 buckets.  However you choose to manage your environment, you may want to implement some sort of automatic check.

JASP Meta: November 2018 Edition

Matt Konda No Comments
  JASP Startup

Building JASP has been a really interesting experience for all of us at Jemurai (https://jasp.cloud).  This post captures some of what I think we’re seeing and learning right now.

We bootstrapped.  Lots of people think raising venture capital for an idea is the best way to build and grow.  We still bootstrapped.  That means we paid for all of the development of the tool by also working our tails off on client projects.  Some of those client projects were cloud security audits, and that’s where the whole idea came from.  Keeping close to clients needs has actually helped us.  Bootstrapping also means we’ve stayed very lean.  Which I hope means the team feels an organic sense of ownership and learning as we go.  We’re hungry.  Not scared.  But hungry.

As consultants and engineers, we are sure there is a lot of value in the JASP tool.  It makes it easy to find and fix AWS security issues.  It provides ongoing visibility and notifications when something isn’t right.  We have learned a lot from the awesome tools listed below in the references and you can run a lot of those by hand to get some of the results JASP gives.  But you have to figure out how to run those tools and digest the results.  We tried to build a tool around a lot of those same ideas that would take away a lot of the complexity of running the tool and exchange that for clearer information and resources for the user.  We believe there is a significant need for easier to use tools that don’t take a dev team or DevOps process and pull them out of the things they know.

I believe someone can get started in JASP in < 10 minutes and get the benefit of daily checking and alerting around AWS security items, for a price that is a small fraction of having someone do it by hand.  – MK

What I am personally learning is that having a tool with value isn’t enough.  I can show people the results, help them fix real security issues and while they are happy to have fixed them – they don’t necessarily feel a clear and present need to buy the tool.  It is still a “nice to have”.  Even most people who might want to try it for free can’t make the time.  People that do try it for free, rarely opt in to purchase unless they need the deeper support we can provide at the higher tiers.  It could be discouraging but I understand, there is so much noise and everyone is so busy, we need to make it easier and find the best ways to communicate about it.

Of course, one big goal was to be able to provide this value in a leveraged way, with a software platform.  We didn’t want to be constrained by what our team could do by hand.  (And of course, we wanted to be a SaaS business with a great hockey stick J curve)  So we’ve been trying to find ways to get the message out about our product.

As we’ve engaged with firms to help us with marketing and growing the idea beyond our technical ideation, we’ve learned more.  We digested their suggestions and it is clear that almost any framing of the tool comes off as a gross oversimplification.  Very common advice is to scare people or drive people to the tool with Compliance.  There’s a place for fear and compliance, but I told myself a long time ago I didn’t want to be one of those security firms.  I hate the idea of using people’s fear and ignorance to get them to buy my tool.  From the beginning, I have wanted to find developers and put tools in their hands that made their lives remarkably better.  I wanted to approach security as a constructive partner, helping people to do the right thing with a positive spin.  I believe by approaching the space this way, we can earn long term trust from partners.

But of course, like I said we also want to grow beyond what we can do directly with word of mouth.  So, following marketing advisors input, we have run ads on various platforms.  We have tried several messaging approaches.  All suggest that what we’re doing isn’t resonating for people in a major way yet.  We have done a couple of feature releases we think might help (better prioritization, user management, dashboards, reporting) but ultimately, we’re at that place where we’re not chasing users by adding more features – we’re trying to find the right messaging, framing, pricing, etc. to make the tool relevant and useful to people.

When I started my career, I hated having to build new features that sales people had already promised to customers.  Now, I’m laughing at my old self and thinking about how hard I would work for each new customer.  That’s not to say I am willing to lose my identity chasing them, but I definitely underestimated the complexity of building and running a software business – in particular the ability to engage with customers and have lots of great learning conversations.  I assumed that with a free tier in the tool we’d be having lots of those conversations.

There is a lot more to do here in terms of building a platform for automation.  The vision has always been to be able to do Azure, GCP and even integrate Glue for source code analysis.  We want to make these kinds of analysis really easy so that they just show up where developers need them.  We want to be an API first backbone for security automation that makes it possible to quickly apply new tools, rules, etc.  Yes, we can build some of these things into one off Jenkins jobs or run them as scripts, but there is a lot more value when the results are aggregated, stored over time, compared to industry standards, and get escalated with support to fix.

It will be very interesting to continue to learn what the industry has an appetite for.  The good news is that it’s all been fun along the way and its the journey not only the destination that matters.

References:

https://github.com/SecurityFTW/cs-suite

https://github.com/toniblyx/my-arsenal-of-aws-security-tools

https://github.com/nccgroup/Scout2

https://github.com/toniblyx/prowler

https://github.com/owasp/glue

JASP Check Deep Dive: ECR

Matt Konda No Comments
  Application Security Cloud Security JASP

As we build JASP, we’re brainstorming and learning about security (so far, primarily in AWS).  This is the first in a series of “Check Deep Dive” posts that talk about things we are checking for in JASP.  It seems like an interesting area to share information.  Incidentally, we’re also going to post more meta posts about the Jemurai and JASP journey.

The first simple check we’ll talk about is around AWS ECR or Elastic Container Registry.  If you are using Docker containers and managing their lifecycle in AWS, you may be using ECR.  You may also be using Docker Hub or other container registries.  This check really demonstrates some of the power of checking security things through an API.  By using the ECR API, we can know some things about the containers hosted in AWS ECR just by asking, the way we do about anything else.

Specifically, we can know the age of the image, any tags and when it was last pushed.  We can easily iterate across regions and find older tagged images.  The idea for most clients we work with is that they want their docker images to be recent.  Older images suggest that they are not patching or updating a given container.  Especially older tagged images are likely places that need to be updated.

Essentially, JASP will check each region for images that are old and alert you to that.

Now, AWS allows you to set lifecycles policies for ECR.  This is a really cool feature.  This can allow you to expire and track this right in AWS.  We totally recommend doing this.  That said, we only have one client that lives this hardcore and actually automatically removes any expired images after every 30 days.  In that case, if they haven’t built an updated image within 30 days, too bad for them.  They’re in it to win it.  And frankly, they are walking the walk there.

On a side note, we have another client that is using Docker heavily and claimed to be patching every 30 days because they pushed new Docker images every 30 days.  When we dove a layer deeper though, we realized that they were hard setting to a very old version of Alpine Linux, which removed many of the benefits of updating frequently.  In other words, they were updating the layer they were building but not the layers they were building on.  To be crystal clear, this “check” won’t identify this issue – you’ll want to look at your dependencies with a tool like dive to do that.

References

https://docs.aws.amazon.com/cli/latest/reference/ecr/index.html

https://docs.aws.amazon.com/AmazonECR/latest/APIReference/API_GetLifecyclePolicy.html

https://docs.aws.amazon.com/AmazonECR/latest/userguide/LifecyclePolicies.html

https://docs.aws.amazon.com/AmazonECR/latest/userguide/lifecycle_policy_examples.html

https://github.com/wagoodman/dive

JASP Dashboards

Matt Konda No Comments
  Cloud Security JASP

JASP is a platform for security automation.  We currently focus on monitoring AWS environments for potential security issues.

Throughout September and October, we have been refining JASP dashboards.  The goal is to give user’s the simplest possible summary view of how they are doing.  We wanted to help convey a sense of how a user’s environment stacks up.  We found that people want to know not just what their issues are but how they are doing … relative to other companies and relative to where they should be.  To do that, we did some number crunching and analysis of typical security issues we find.

To implement this, we drew a bit from SSL Labs Grades and Github contributions.

The goal was to show:

  • Roughly how you are doing in simple terms.  (Nobody wants a “D”!)
  • How we calculated your grade and some history so you can see improvement or quality sliding right there in the dashboard
  • Our “The One Thing™” idea – the one thing you should go fix today.

Below is an example from one of the intentionally insecure environments we test with.  Note that the services are clickable and drill through to the specific security issues that are causing the grade for the given service.

JASP users should be able to see their dashboards now at:  https://app.jasp.cloud/#/dashboard/.  We are adding notifications now to alert users if their grade changes and to ensure that we communicate what their grades are periodically.

Live Coding a Glue Task at AppSecUSA – Video

Matt Konda No Comments
  Application Security OpenSource

Here is the video from the Glue and live coding talk at AppSecUSA.

Live Coding a New Glue Task at AppSecUSA

Matt Konda No Comments
  Application Security Engineering OpenSource

At AppSecUSA, OWASP Glue, a project we contribute heavily to, was asked to present in the project showcase.  I put together an overview talk about how the tool is architected and what we use it for.  Then, we added a new task live during the talk.  I thought that was enough fun that it was worth blogging about.  I would like to start by acknowledging Omer Levi Hevroni, of Soluto, who has also contributed and continued support for Glue over the past year +.

Why Glue?

We started working on Glue because we found that most security tools don’t integrate all that cleanly with CI/CD or other developer oriented systems.  Our goal is always to get the checking as close to the time of code authorship as possible.  Further, we thought that having it wrap open source tools would make them more accessible.  Philosophically, I’m not that excited about tools (see earlier post) but we should definitely be using them as much as we can provided they don’t get in our way.  Ultimately, we wrote Glue to make it easy to run a few security tools and have the output dump into Jira or a CSV.

Glue Architecture

So we defined Glue with the following core concepts:

  • Mounters – these make the code available.  Could be pulling from GitHub or opening a docker image.
  • Tasks:  Files – these analyze files.  Examples are ClamAV and hashdeep.
  • Tasks:  Code – these are some level of code analysis.  Some are first class static tools, others are searching for dependencies or secrets.
  • Tasks:  Live – these are running some kind of live tool like Zap against a system.
  • Filters – filters take the list of findings and eliminate some based on whatever the filter criteria are.  An example is limiting ZAP X-Frame-Options findings to 1 per scan instead of 1 per page.
  • Reporters – reporters take the list of findings and push them wherever you want them to end up.  JIRA, TeamCity, Pivotal Tracker, CSV, JSON, etc.

Live Coding

OK, so a common task is to say

“Hey, this is cool but I want to do this with a tool that Glue doesn’t yet support.”

Great.  That should be easy.  Let’s walk through the steps.  We’ll do this with a python dependency checker:  safety.

Set Up the Task

A good way to start a new task is to copy an existing one.  In this case, we’re going to do a python based task so let’s copy the bandit task and alter to match:

require 'glue/tasks/base_task'  # We need these libraries.  The base_task gives us a report method.
require 'json'
require 'glue/util'
require 'pathname'

# This was written live during AppSecUSA 2018.
class Glue::SafetyCheck < Glue::BaseTask  # Extend the base task.

Glue::Tasks.add self  # Glue is dynamic and discovers tasks based on a static list.  This adds this task to the list.
includeGlue::Util

def initialize(trigger, tracker)
  super(trigger, tracker)
  @name="SafetyCheck"                             # This is the name of the check.  -t safetycheck (lowered)
  @description="Source analysis for Python"
  @stage= :code                                   # Stage indicates when the check should run.
  @labels<<"code"<<"python"                       # The labels allow a user to run python related tasks.  -l python
end

Now we have the start of a class that defines our SafetyCheck task.

Run the Tool :  Safety Check

Here we just need to implement the run method and tell the task how to call the tool we want to run.  In this case, we want it to create json.  The resulting code is:

def run
  rootpath = @trigger.path
  @result=runsystem(true, "safety", "check", "--json", "-r", "#{rootpath}/requirements.txt")
end

The @trigger is set when the Task is initialized (see above) and includes information about where the code is.  We use that to know where the path that we want to analyze is.  Then we use one of the provided util methods runsystem to invoke safety check with the parameters we want.

Note that we are putting the result in the @result instance variable.

Parse the Results

Once the tool runs, we have the output in our @result variable.  So we can look at it and parse out the JSON as follows:

 def analyze
    puts @result
    results = clean_result(@result)
    begin
      parsed = JSON.parse(results)
      parsed.each do |item|  
        source = { :scanner => @name, :file => "#{item[0]} #{item[2]} from #{@trigger.path}/requirements.txt", :line => nil, :code => nil }
        report "Library #{item[0]} has known vulnerabilities.", item[3], source, severity("medium"), fingerprint(item[3]) 
      end 
    rescue Exception => e
      Glue.warn e.message
      Glue.warn e.backtrace
      Glue.warn "Raw result: #{@result}"
    end
  end

Here, we call a clean_result method on the result first.  You can look here for detail, but it is just pulling the warnings that the tool emits that make the output not valid JSON.  We do this to make sure the JSON is parseable for our output.  This is a common issue with open source tools.  I don’t underestimate the value of making these things just work.

The magic here is really in the two lines that set the source and then report it.  The source in Glue terms is the thing that found the issue and where it found it.  In this case, we’re setting it to be our Task (SafetyCheck) and the library name in the output from file containing the dependencies. (requirements.txt)

The report method is supplied by the parent BaseTask class and takes as arguments:  description, detail, source, severity and fingerprint.

def report description, detail, source, severity, fingerprint
    finding = Glue::Finding.new( @trigger.appname, description, detail, source, severity, fingerprint, self.class.name )
    @findings << finding
end

You can see if you look closely that we set all of the severities to Medium here because safety check doesn’t give us better information.  We also use the supplied fingerprint method to make sure that we know if we have a duplicate.  You can also see that the result of calling report is that we have a new finding and the finding is added to the array of findings that were created by this task.  We get the trigger name, the check name, a timestamp, etc. just by using the common Finding and BaseTask classes.

Making Sure the Tool is Available

In addition to run and analyze the other method we expect to have in our Task is a method called supported?.  The purpose of this method is to check that the tool is available.  Here’s the implementation we came up with for safety check, which doesn’t have a version call from the CLI.

 def supported?
    supported=runsystem(true, "safety", "check", "--help")
    if supported =~ /command not found/
      Glue.notify "Install python and pip."
      Glue.notify "Run: pip install safety"
      Glue.notify "See: https://github.com/pyupio/safety"
      return false
    else
      return true
    end
  end

The idea here is to run the tool in a way that tests if it is available and alerts the user if it is not.  Graceful degredation as it were…

The Meta

Here is the code from the Tasks ruby file that runs all the tasks.

if task.stage == stage and task.supported?
  if task.labels.intersect? tracker.options[:labels] or      # Only run tasks with labels
    ( run_tasks and run_tasks.include? task_name.downcase )  # or that are explicitly requested.

    Glue.notify "#{stage} - #{task_name} - #{task.labels}"
    task.run
    task.analyze
    ...

Here you see the supported?, run and analyze methods getting called.  You also see the labels and tasks being applied.  Its not magic, but it might look weird when you first jump in.

Conclusion

We wanted to create a new task.  We did it in about 20 minutes in a talk.  Of course, I wrote the code around it so it was easy.  But it does illustrate how simple it is to add a new task to Glue.

If you want to see this check in action, you can run something like:

bin/glue -t safetycheck https://github.com/DefectDojo/django-DefectDojo

We’re getting the Docker image updated, but once available you can run:

docker run owasp/glue -t safetycheck https://github.com/DefectDojo/django-DefectDojo

We hope people can digest and understand Glue.  We think it is an easy way to get a lot of automation running quickly.  We even run a cluster of Kubernetes nodes doing analysis.  This in addition to integrating into Jenkins builds or git hooks.  Happy Open Source coding!!!