diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 0d31019..0000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.sass-cache/ diff --git a/_site/2015/02/21/hello_world.html b/2015/02/21/hello_world.html similarity index 100% rename from _site/2015/02/21/hello_world.html rename to 2015/02/21/hello_world.html diff --git a/README.md b/README.md deleted file mode 100644 index 9f5daeb..0000000 --- a/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Published at [http://l01cd3v.github.io](http://l01cd3v.github.io) - - $ # Commit a new blog post - $ # Run jekyll to create new _site files - $ jekyll serve - $ # Commit new _site on source branch - $ git add _site - $ git push origin source - $ git subtree push --prefix _site origin master diff --git a/_config.yml b/_config.yml deleted file mode 100644 index b9dd3b4..0000000 --- a/_config.yml +++ /dev/null @@ -1,40 +0,0 @@ -# -# This file contains configuration flags to customize your site -# - -# Name of your site (displayed in the header) -name: l01cd3v.github.io -author: Loïc - -# Short bio or description (displayed in the header) -description: - -# Pagination -paginate: 5 - -# Includes an icon in the footer for each username you enter -footer-links: - github: l01cd3v - linkedin: loicsimon - rss: f00 # just type anything here for a working RSS icon, make sure you set the "url" above! - -# Enter your Google Analytics web tracking code (e.g. UA-2110908-2) to activate tracking -google_analytics: UA-56674561-3 - -# Used for Sitemap.xml and your RSS feed -url: http://l01cd3v.github.io - -# -# !! You don't need to change any of the configuration flags below !! -# - -markdown: redcarpet -#highlighter: pygments -gems: [jekyll-paginate] - -# Exclude these files from your production _site -exclude: - - Gemfile - - Gemfile.lock - - LICENSE - - README.md diff --git a/_includes/analytics.html b/_includes/analytics.html deleted file mode 100644 index d8cdc0e..0000000 --- a/_includes/analytics.html +++ /dev/null @@ -1,13 +0,0 @@ -{% if site.google_analytics %} - - - -{% endif %} diff --git a/_includes/isec.html b/_includes/isec.html deleted file mode 100644 index d5ec1ee..0000000 --- a/_includes/isec.html +++ /dev/null @@ -1,3 +0,0 @@ -
- [Originally published on iSEC Partners's research blog] -
diff --git a/_includes/ncc.html b/_includes/ncc.html deleted file mode 100644 index 8badbeb..0000000 --- a/_includes/ncc.html +++ /dev/null @@ -1,3 +0,0 @@ -
- [Originally published on NCC Group's blog] -
diff --git a/_includes/post_body.html b/_includes/post_body.html deleted file mode 100644 index 306f3b2..0000000 --- a/_includes/post_body.html +++ /dev/null @@ -1,37 +0,0 @@ -
- - {% if post %} - {% assign tmp = post %} - {% else %} - {% assign tmp = page %} - {% endif %} - -

- {% if post %} - {{ tmp.title }} - {% else %} - {{ tmp.title }} - {% endif %} -

- -
- Published on {{ tmp.date | date: "%B %e, %Y" }} -
- - {% if tmp.tags contains 'iSEC' %} - {% include isec.html %} - {% endif %} - - {% if tmp.tags contains 'NCC' %} - {% include ncc.html %} - {% endif %} - -
- {% if post %} - {{ post.content }} - {% else %} - {{ content }} - {% endif %} -
- -
diff --git a/_includes/svg-icons.html b/_includes/svg-icons.html deleted file mode 100644 index 18deccd..0000000 --- a/_includes/svg-icons.html +++ /dev/null @@ -1,11 +0,0 @@ -{% if site.footer-links.dribbble %}{% endif %} -{% if site.footer-links.email %}{% endif %} -{% if site.footer-links.facebook %}{% endif %} -{% if site.footer-links.flickr %}{% endif %} -{% if site.footer-links.github %}{% endif %} -{% if site.footer-links.instagram %}{% endif %} -{% if site.footer-links.linkedin %}{% endif %} -{% if site.footer-links.pinterest %}{% endif %} -{% if site.footer-links.rss %}{% endif %} -{% if site.footer-links.twitter %}{% endif %} -{% if site.footer-links.stackoverflow %}{% endif %} diff --git a/_layouts/archives.html b/_layouts/archives.html deleted file mode 100644 index df6ac9f..0000000 --- a/_layouts/archives.html +++ /dev/null @@ -1,13 +0,0 @@ ---- -layout: default ---- - -

{{ page.title }}

- - -
- -{{ content }} diff --git a/_layouts/category_index.html b/_layouts/category_index.html deleted file mode 100644 index 855a404..0000000 --- a/_layouts/category_index.html +++ /dev/null @@ -1,10 +0,0 @@ ---- -layout: archives ---- - -
-{% for post in site.categories[page.category] %} -
{{ post.title }}
-
{{ post.date | date_to_string }}
-{% endfor %} -
diff --git a/_layouts/default.html b/_layouts/default.html deleted file mode 100644 index acf28b6..0000000 --- a/_layouts/default.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - - - - - - - - - {% if page.title %} {{page.title}} – {{site.name}} {% else %} {{site.name}} {% endif %} - - - -
-
-
-

{{site.name}}

-

by {{site.author}}

- -
-
-
- {{ content }} -
- -
-
- {% include analytics.html %} - - diff --git a/_layouts/post.html b/_layouts/post.html deleted file mode 100644 index 8b7dac1..0000000 --- a/_layouts/post.html +++ /dev/null @@ -1,5 +0,0 @@ ---- -layout: default ---- - -{% include post_body.html %} diff --git a/_plugins/generate_categories.rb b/_plugins/generate_categories.rb deleted file mode 100644 index 7cbeb5e..0000000 --- a/_plugins/generate_categories.rb +++ /dev/null @@ -1,240 +0,0 @@ -# encoding: utf-8 -# -# Jekyll category page generator. -# http://recursive-design.com/projects/jekyll-plugins/ -# -# Version: 0.2.4 (201210160037) -# -# Copyright (c) 2010 Dave Perrett, http://recursive-design.com/ -# Licensed under the MIT license (http://www.opensource.org/licenses/mit-license.php) -# -# A generator that creates category pages for jekyll sites. -# -# To use it, simply drop this script into the _plugins directory of your Jekyll site. You should -# also create a file called 'category_index.html' in the _layouts directory of your jekyll site -# with the following contents (note: you should remove the leading '# ' characters): -# -# ================================== COPY BELOW THIS LINE ================================== -# --- -# layout: default -# --- -# -#

{{ page.title }}

-# -# ================================== COPY ABOVE THIS LINE ================================== -# -# You can alter the _layout_ setting if you wish to use an alternate layout, and obviously you -# can change the HTML above as you see fit. -# -# When you compile your jekyll site, this plugin will loop through the list of categories in your -# site, and use the layout above to generate a page for each one with a list of links to the -# individual posts. -# -# You can also (optionally) generate an atom.xml feed for each category. To do this, copy -# the category_feed.xml file to the _includes/custom directory of your own project -# (https://github.com/recurser/jekyll-plugins/blob/master/_includes/custom/category_feed.xml). -# You'll also need to copy the octopress_filters.rb file into the _plugins directory of your -# project as the category_feed.xml requires a couple of extra filters -# (https://github.com/recurser/jekyll-plugins/blob/master/_plugins/octopress_filters.rb). -# -# Included filters : -# - category_links: Outputs the list of categories as comma-separated links. -# - date_to_html_string: Outputs the post.date as formatted html, with hooks for CSS styling. -# -# Available _config.yml settings : -# - category_dir: The subfolder to build category pages in (default is 'categories'). -# - category_title_prefix: The string used before the category name in the page title (default is -# 'Category: '). -module Jekyll - - # The CategoryIndex class creates a single category page for the specified category. - class CategoryPage < Page - - # Initializes a new CategoryIndex. - # - # +template_path+ is the path to the layout template to use. - # +site+ is the Jekyll Site instance. - # +base+ is the String path to the . - # +category_dir+ is the String path between and the category folder. - # +category+ is the category currently being processed. - def initialize(template_path, name, site, base, category_dir, category) - @site = site - @base = base - @dir = category_dir - @name = name - - self.process(name) - - if File.exist?(template_path) - @perform_render = true - template_dir = File.dirname(template_path) - template = File.basename(template_path) - # Read the YAML data from the layout page. - self.read_yaml(template_dir, template) - self.data['category'] = category - # Set the title for this page. - title_prefix = site.config['category_title_prefix'] || 'Category: ' - self.data['title'] = "#{title_prefix}#{category}" - # Set the meta-description for this page. - meta_description_prefix = site.config['category_meta_description_prefix'] || 'Category: ' - self.data['description'] = "#{meta_description_prefix}#{category}" - else - @perform_render = false - end - end - - def render? - @perform_render - end - - end - - # The CategoryIndex class creates a single category page for the specified category. - class CategoryIndex < CategoryPage - - # Initializes a new CategoryIndex. - # - # +site+ is the Jekyll Site instance. - # +base+ is the String path to the . - # +category_dir+ is the String path between and the category folder. - # +category+ is the category currently being processed. - def initialize(site, base, category_dir, category) - template_path = File.join(base, '_layouts', 'category_index.html') - super(template_path, 'index.html', site, base, category_dir, category) - end - - end - - # The CategoryFeed class creates an Atom feed for the specified category. - class CategoryFeed < CategoryPage - - # Initializes a new CategoryFeed. - # - # +site+ is the Jekyll Site instance. - # +base+ is the String path to the . - # +category_dir+ is the String path between and the category folder. - # +category+ is the category currently being processed. - def initialize(site, base, category_dir, category) - template_path = File.join(base, '_includes', 'custom', 'category_feed.xml') - super(template_path, 'atom.xml', site, base, category_dir, category) - - # Set the correct feed URL. - self.data['feed_url'] = "#{category_dir}/#{name}" if render? - end - - end - - # The Site class is a built-in Jekyll class with access to global site config information. - class Site - - # Creates an instance of CategoryIndex for each category page, renders it, and - # writes the output to a file. - # - # +category+ is the category currently being processed. - def write_category_index(category) - target_dir = GenerateCategories.category_dir(self.config['category_dir'], category) - index = CategoryIndex.new(self, self.source, target_dir, category) - if index.render? - index.render(self.layouts, site_payload) - index.write(self.dest) - # Record the fact that this pages has been added, otherwise Site::cleanup will remove it. - self.pages << index - end - - # Create an Atom-feed for each index. - feed = CategoryFeed.new(self, self.source, target_dir, category) - if feed.render? - feed.render(self.layouts, site_payload) - feed.write(self.dest) - # Record the fact that this pages has been added, otherwise Site::cleanup will remove it. - self.pages << feed - end - end - - # Loops through the list of category pages and processes each one. - def write_category_indexes - if self.layouts.key? 'category_index' - self.categories.keys.each do |category| - self.write_category_index(category) - end - - # Throw an exception if the layout couldn't be found. - else - throw "No 'category_index' layout found." - end - end - - end - - - # Jekyll hook - the generate method is called by jekyll, and generates all of the category pages. - class GenerateCategories < Generator - safe true - priority :low - - CATEGORY_DIR = 'categories' - - def generate(site) - site.write_category_indexes - end - - # Processes the given dir and removes leading and trailing slashes. Falls - # back on the default if no dir is provided. - def self.category_dir(base_dir, category) - base_dir = (base_dir || CATEGORY_DIR).gsub(/^\/*(.*)\/*$/, '\1') - category = category.gsub(/_|\P{Word}/, '-').gsub(/-{2,}/, '-').downcase - File.join(base_dir, category) - end - - end - - - # Adds some extra filters used during the category creation process. - module Filters - - # Outputs a list of categories as comma-separated links. This is used - # to output the category list for each post on a category page. - # - # +categories+ is the list of categories to format. - # - # Returns string - def category_links(categories) - base_dir = @context.registers[:site].config['category_dir'] - categories = categories.sort!.map do |category| - category_dir = GenerateCategories.category_dir(base_dir, category) - # Make sure the category directory begins with a slash. - category_dir = "/#{category_dir}" unless category_dir =~ /^\// - "#{category}" - end - - case categories.length - when 0 - "" - when 1 - categories[0].to_s - else - categories.join(', ') - end - end - - # Outputs the post.date as formatted html, with hooks for CSS styling. - # - # +date+ is the date object to format as HTML. - # - # Returns string - def date_to_html_string(date) - result = '' + date.strftime('%b').upcase + ' ' - result += date.strftime('%d ') - result += date.strftime('%Y ') - result - end - - end - -end \ No newline at end of file diff --git a/_posts/2015-02-21-hello_world.markdown b/_posts/2015-02-21-hello_world.markdown deleted file mode 100644 index b38cedd..0000000 --- a/_posts/2015-02-21-hello_world.markdown +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: post -title: Intro -date: 2015-02-21 17:00 ---- - -I'm a Security Engineer and, on this blog, I will talk about research projects -I work on, tools that I write and publish, and miscellaneous security and -privacy related subjects. diff --git a/_posts/2015-02-22-announcing_the_aws_blog_post_series.markdown b/_posts/2015-02-22-announcing_the_aws_blog_post_series.markdown deleted file mode 100644 index 4d6288e..0000000 --- a/_posts/2015-02-22-announcing_the_aws_blog_post_series.markdown +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: post -title: "Announcing the AWS blog post series" -date: 2015-02-22 22:24 -post_author: Loïc Simon -categories: AWS -tags: iSEC ---- - -Starting this month, iSEC Partners will start a series of blog posts related to -AWS. The goal of these blog posts will be to: - -* Discuss common security gaps in AWS environments -* Discuss common security gaps in the architecture of applications deployed in - the cloud -* Describe methods and tools used to identify these security gaps -* Share tools and scripts that facilitate daily and secure work with AWS -* Share AWS policies that help improve the security posture of AWS environments - -To share material, iSEC created a new public -AWS-recipes repository -on Github. The tools and policies shared in this repository will be discussed -and explained in dedicated blog articles. - -Because iSEC has been assessing the security of AWS environment for several -years, we have a number of ideas and articles in the pipe awaiting to be -written and published. Our target goal is to publish at least on a bi-monthly -basis at the beginning of the project, and adjust this publication rate after -we catch up. - -Without further due, we will start this series with articles that discuss -Identity and Access Management (IAM) common issues and best practices, and will -present a strategy to improve one's security posture when using AWS. diff --git a/_posts/2015-02-23-do_not_use_your_root_account.markdown b/_posts/2015-02-23-do_not_use_your_root_account.markdown deleted file mode 100644 index 618f8ba..0000000 --- a/_posts/2015-02-23-do_not_use_your_root_account.markdown +++ /dev/null @@ -1,55 +0,0 @@ ---- -layout: post -title: "Do not use your AWS root account" -date: 2015-02-23 08:42 -post_author: Loïc Simon -categories: AWS -tags: iSEC ---- - -### What is the AWS root account? - -The AWS root account is the account that was used -- or created -- when signing -up with Amazon Web Services. This account has full access to all resources in -the account and it is not possible to alter this configuration. - -### Risks of using the AWS root account - -Using the AWS root account means that there is potential for its compromise. -In particular, iSEC noticed that AWS customers who use the AWS root account -tend to do the following: - -1. Share credentials between employees. -1. Disable Multi-Factor Authentication (MFA) for convenience. - -Shared credentials, aside from increasing the risk of compromise during the -sharing process, render credential rotation impractical due to the need for the -newly-generated secret to be known by multiple parties. Sharing the AWS root -account also undermines any effort towards using IAM and leveraging the -fine-grained access controls it offers. Finally, shared credentials result in -loss of the attribution ability, which makes auditing harder and may prevent -successful investigation. - -### AWS Identity and Access Management (IAM) - -AWS IAM allows account administrators to create users for every employee and -grant them access to a limited set of services, actions, and resources. This -allows AWS account administrators to apply the principle of least privilege, -which dictates that a given user should only be able to access the information -and resources that are necessary for them to perform tasks they are responsible -for. Additionally, use of IAM allows AWS users to rotate credentials and revoke -privileges without impacting other employees. - -AWS account administrators should create an *Administrator* IAM group, grant -administrator privileges to this group, and create individual IAM users for -each employee in charge of administrating the AWS account. When done, the AWS -root password should be rotated and stored in a safe manner. Furthermore, -additional credentials such as access keys and certificates should be deleted. - -### Important security consideration about the root account - -AWS users should always enable MFA on their root account, even when the -password is securely stored; it is important to realize that the password reset -for the root account process only requires access to the email address -associated with this account. **This means that, without MFA, your production -environment is only as secure as an email.** diff --git a/_posts/2015-02-24-iam_user_management.markdown b/_posts/2015-02-24-iam_user_management.markdown deleted file mode 100644 index 50ef99a..0000000 --- a/_posts/2015-02-24-iam_user_management.markdown +++ /dev/null @@ -1,140 +0,0 @@ ---- -layout: post -title: "IAM user management strategy" -date: 2015-02-24 20:49 -post_author: Loïc Simon -categories: AWS -tags: iSEC ---- - -### Use IAM groups - -When granting privileges to IAM users, AWS account administrators should avoid -use of user-specific policies. Instead, create groups whose name explicitly -defines the members' job functions or responsibilities (*e.g.* AWS -Administrators, Operations, Developers, Accountants), and define the -permissions granted within group policies. Doing so will simplify the -permissions management process as changes in group policies apply to all -members. - -When performing AWS configuration reviews, iSEC often discovers IAM users -whose privileges have been granted via a combination of IAM user and IAM group -policies. It is not uncommon to see IAM users who are granted full -administrator privileges in a redundant manner, via both user and group -policies. Such configuration creates an avenue for configuration mistakes, as -another administrator may believe that terminating an IAM user's membership to -the admin group is sufficient. Therefore, banning use of IAM user policies -will result in making one's AWS environment less error-prone. - -***Note***: It is on purpose that iSEC recommends using IAM group names that -reflect a job title or responsibility. IAM users who do not fit in such groups -(*e.g.* headless users) should not exist. Instead, AWS account administrators -should investigate use of IAM roles for EC2. Further details will be discussed -in an upcoming blog post. - -### Create a common IAM group to apply generic policies - -Because a number of policies must be applied to all users, iSEC recommends that -AWS account administrators create an IAM group that all IAM users belong to. -Doing so will allow AWS account administrators to consistently grant privileges -and enforce a number of rules. - -***Note***: It is important that all IAM users belong to this common IAM group -to ensure that policies are consistently applied. Failure to do so will create -gaps in one's AWS environment security posture. - -### Authorize IAM users to manage their credentials - -To begin with, iSEC recommends that AWS account administrators allow all of -their IAM users to manage their credentials, and only theirs. With all IAM -users belonging to the common IAM group, this can be achieved by applying the -following IAM policy to the group. - - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "iam:*AccessKey*", - "iam:*Password", - "iam:*MFADevice*", - "iam:UpdateLoginProfile" - ], - "Resource": "arn:aws:iam::AWS_ACCOUNT_ID:user/${aws:username}" - }, - { - "Effect": "Allow", - "Action": [ - "iam:CreateVirtualMFADevice", - "iam:DeleteVirtualMFADevice" - ], - "Resource": "arn:aws:iam::AWS_ACCOUNT_ID:mfa/${aws:username}" - } - ] - } - -While the above policy is sufficient to allow users to manage their -credentials, AWS administrators may consider the following statement as an -addition; it allows IAM users to know what the account's password policy is. - - { - "Effect": "Allow", - "Action": "iam:GetAccountPasswordPolicy", - "Resource": "*" - } - -### Use AWS Scout2 to detect user policies - -The default ruleset used by [AWS -Scout2](https://github.com/iSECPartners/Scout2) includes a rule that checks for -user policies and reports the use of user-specific IAM policies as a warning. -Detection of user-specific IAM policies results in the IAM menu dropdown -containing a "User policies" security risk, as illustrated in the below screenshot. - -![Screenshot: IAM menu dropdown with a User policies security risk](/images/aws/awsscout2-iam-user-policy-1.png) - -When clicked-on, this "User policies" link filters the list of IAM users to only -display those who have at least one user policy attached. The orange badge -indicates a warning and the count of user policies attached to this particular -IAM user. - -![Screenshot: Orange badge indicating that at least one user policy is attached to that IAM user](/images/aws/awsscout2-iam-user-policy-2.png) - -### Check that all IAM users belong to the common group - -AWS Scout2 comes with a tool — RulesGenerator.py — that allows AWS account -administrators to generate a custom ruleset to tailor the report to their -needs. An optional IAM rule requires all IAM users to belong to a common IAM -group. In order to enable this rule, the following can be done: - -1. Run the rules generator with the following command line: -
./RulesGenerator.py --ruleset_name isec --services iam
-1. Answer "yes" to the question "Would you like to ensure that all IAM users belong to a given IAM group?" -1. Enter the name of your common group (*e.g.* AllUsers) -1. Enter "yes" or "y" to confirm -1. Change the level if desired -1. Run Scout2 - -***Note***: If you have already run Scout2 and do not wish to download the latest -IAM configuration, use the following command to run an offline analysis: - - ./Scout2.py --ruleset_name isec --services iam --local - -The following screenshot illustrates the IAM menu dropdown containing a -security risk when IAM users do not belong to the configured common group. - -![Screenshot: IAM menu dropdown when IAM users do not belong to the common group](/images/aws/awsscout2-iam-user-commongroup-1.png) - -When clicked-on, this link filters the list of IAM users to only display those -who do not belong to the common IAM group. A colored warning sign appears, -warning about this issue. - -![Screenshot: Orange badge indicating that at least one user policy is attached to that IAM user](/images/aws/awsscout2-iam-user-commongroup-2.png) - -### Conclusion - -Strict management of IAM users and tight control of their privileges is key in -maintaining a secure AWS environment. When followed, the above recommendations -should enable AWS administrators to manage IAM users with improved efficiency -and lower the chances of overly privileged users to exist. diff --git a/_posts/2015-04-02-use_and_enforce_mfa.markdown b/_posts/2015-04-02-use_and_enforce_mfa.markdown deleted file mode 100644 index 9272f20..0000000 --- a/_posts/2015-04-02-use_and_enforce_mfa.markdown +++ /dev/null @@ -1,144 +0,0 @@ ---- -layout: post -title: "Use and enforce Multi-Factor Authentication" -date: 2015-04-02 14:10 -post_author: Loïc Simon -categories: AWS -tags: iSEC ---- - -### What is Multi-Factor Authentication? - -When enabled, Multi-Factor Authentication (MFA) provides strong -defense-in-depth against compromises of credentials. MFA-enabled users have a -device that periodically generates a new authentication code (*i.e.* one-time -password); they need to enter the current authentication code along with their -static credentials (*i.e.* username and password) in order to successfully -authenticate. In addition to supporting MFA when accessing the web console -(*i.e.* password-based authentication), AWS also offers MFA-protected API -access for users who work with AWS access keys. Through the Security Token -Service (STS), IAM users can request temporary credentials in exchange for -their long-lived credentials (*i.e.* AWS access key ID and secret key) and -their current authentication code. - -### Why should one use and enforce MFA? - -For companies deploying their application in the cloud, a breach that results -in unauthorized access to the management console — or API — is the -worst-case scenario. While a number of AWS administrators have realized the -importance of enabling MFA when they access the web console, a limited number -of them enforce MFA-protected API access. This represents a huge gap in one's -security posture because AWS access keys do not come with as many security -features as passwords do: - -* AWS administrators can enforce password expiration; this is currently not -possible for AWS access keys. -* While it is probably safe to assume that most AWS administrators do not store -their password in plaintext, most of them use AWS access keys. By design, these -keys are meant to be stored in plaintext files that are accessed by tools built -with the various AWS SDKs. -* A lost password is a forgotten password; a lost key is a key stored in a lost -file, which may be on an unencrypted storage device (e.g. hard drive or USB Flash -drive). - -Because AWS access keys are long-lived credentials that are stored in plaintext -files, they are more susceptible to compromise than passwords. It is therefore -necessary to enable MFA when the AWS API is accessed using these keys and not -only when users sign in using their passwords. - -### How can one enforce MFA? - -Unfortunately, at time of writing, AWS does not offer an option to enforce -MFA-protected API access via a global setting. Therefore, AWS account -administrators must carefully manage their IAM users and develop a strategy to -reliably achieve this. In order to enforce MFA-protected API access, iSEC -recommends the following: - -1. Create a common IAM group that all IAM users belong to, as discussed in the previous [IAM user management strategy]({{ site.baseurl }}/aws/2015/02/24/iam_user_management.html) post. -1. Add the following policy (also available on [Github](https://github.com/iSECPartners/AWS-recipes/blob/master/IAM-Policies/EnforceMFA-8HourSession.json)) to enforce MFA for all users who belong to this group. - - -This policy will enforce MFA regardless of how the IAM user authenticated with -AWS; it will be effective whether they use password-based or key-based -authentication. - - { - "Version": "2012-10-17", - "Statement": [ - { - "Effect": "Deny", - "Action": "*", - "Resource": "*", - "Condition": { - "Null":{"aws:MultiFactorAuthAge":"true"} - } - }, - { - "Effect": "Deny", - "Action": "*", - "Resource": "*", - "Condition": { - "NumericGreaterThan":{"aws:MultiFactorAuthAge":"28800"} - } - } - ] - } - - -The first statement in the above policy denies all actions if the -*aws:MultiFactorAuthAge* key is not present; this key only exists if MFA is -used [1]. - -The second statement verifies that the validation of the MFA code was performed -less than eight hours ago. Temporary credentials may be valid for a duration -between fifteen minutes and thirty-six hours [2]. iSEC recommends requiring -users to initiate a new session at least once a day. - -***Note:*** An "explicit deny" means that, regardless of other policies granted -to a user, this deny rule will prevail. More information about the IAM policy -evaluation logic can be found in the AWS documentation at http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_EvaluationLogic.html. - -### Use AWS Scout2 to detect users without MFA - -The default ruleset used by [AWS Scout2](https://github.com/iSECPartners/Scout2) includes a rule that checks for IAM users who have password-based authentication enabled but do not have an MFA device configured. If Scout2 detects IAM users with password-based authentication enabled and no MFA device, it will document a "Lack of MFA" security risk in the IAM menu dropdown, as illustrated in the below screenshot. - -![Screenshot: IAM menu dropdown with a "Lack of MFA" security risk](/images/aws/awsscout2-user-nomfa-1.png) - -When clicked, this "Lack of MFA" link filters the list of IAM users to display -those who have password-based authentication enabled but no MFA device -configured. The red "No" following "Multi-Factor enabled" indicates a danger -tied to that particular IAM user. - -![Screenshot: Red "No" indicating that this IAM user may access the web console without MFA](/images/aws/awsscout2-user-nomfa-2.png) - -### How can one use MFA with command line tools? - -Users of the AWS CLI (and other command line tools) have several methods to -configure their credentials, such as environment variables, configuration -files, or command line arguments. However, updating these settings on a daily -basis when MFA-protected API access is enabled is inconvenient. To help -facilitate this work flow, iSEC has created a set of Python tools and released -them in the [AWS-recipes](https://github.com/iSECPartners/AWS-recipes) -repository. Further details about these tools will be published in the next -blog post. - -Additional information about MFA with AWS is available in the AWS -documentation at -https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_ManagingMFA.html. - - -### Conclusion - -Enforcing Multi-Factor Authentication for all IAM users is extremely important -in order to mitigate the risks of credentials compromise (especially the AWS -access key ID and secret). This aspect of security is commonly overlooked and -may result in catastrophic damages. By using a strict strategy for management -of IAM users and the above IAM policy, AWS administrators may significantly -reduce risks of account compromise. - -[1] http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#AvailableKeys - -[2] http://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html diff --git a/_posts/2015-04-03-session_management_tools.markdown b/_posts/2015-04-03-session_management_tools.markdown deleted file mode 100644 index b9e6b2b..0000000 --- a/_posts/2015-04-03-session_management_tools.markdown +++ /dev/null @@ -1,112 +0,0 @@ ---- -layout: post -title: "Work daily with enforced MFA-protected API access" -date: 2015-04-03 14:10 -post_author: Loïc Simon -categories: AWS -tags: iSEC ---- - -### AWS Security Token Service - -The AWS Security Token Service (STS) is the gateway used to create sessions -when MFA-protected API access is enabled. This service allows IAM users to -retrieve short-lived credentials (*i.e* access key ID, secret access key, and -session token) in exchange for their long-lived credentials (*i.e.* AWS access -key ID and secret key) and their current authentication code. When enforcing -MFA-protected API access, as recommended in the previous [Use and enforce -Multi-Factor -Authentication]({{site.baseurl}}/aws/2015/04/02/use_and_enforce_mfa.html) post, -IAM users must use these short-lived credentials to access other AWS services. - -### Challenges with MFA-protected API access - -When MFA-protected API access is enforced, managing AWS access keys becomes -challenging because configuration files that contain these credentials must be -updated regularly. Users must also ensure that they do not lose their -long-lived credentials when modifying the configuration files to write their -short-lived credentials. In order to help with this workflow, iSEC wrote and -released several simple tools in the -[AWS-recipes](https://github.com/iSECPartners/AWS-recipes) repository. - -The collection of tools that we will discussed below uses the "new -and standardized way to manage credentials in the AWS SDKs", meaning that -SDKs are expecting to read credentials from the *.aws/credentials* file under -the user's home or profile directory. - -### aws\_recipes\_configure\_iam.py - -The -[aws\_recipes\_configure\_iam.py](https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_configure_iam.py) -tool allows users to configure and store their long-lived credentials in a new, -non-standard, *.aws/credentials.no-mfa* file. In addition to prompting for the -AWS access key ID and secret key, this tool also prompts for the MFA device -serial number because this information must be provided when making calls to -the STS API. Similar to the AWS CLI and SDKs, it supports profile names. The -following code snippet is an example of calling this tool to configure a new -profile called *isecpartners*: - - $ ./aws_recipes_configure_iam.py --profile isecpartners - AWS Access Key ID: AWS_KEY_ID - AWS Secret Access Key: AWS_SECRET_KEY - AWS MFA serial: arn:aws:iam::AWS_ACCOUNT_ID:mfa/USER_NAME - -When looking at the *.aws* folder, we can see that a *credentials.no-mfa* file -exists and that it contains the credentials that were just entered: - - $ ls -l ~/.aws - total 4 - -rw-r--r-- 1 loic loic 93 Apr 3 14:00 credentials.no-mfa - $ cat ~/.aws/credentials.no-mfa - [isecpartners] - aws_access_key_id = AWS_KEY_ID - aws_secret_access_key = AWS_SECRET_KEY - aws_mfa_serial = arn:aws:iam::AWS_ACCOUNT_ID:mfa/USER_NAME - -Now that long-lived credentials are configured, we can use the next tool to -call the AWS STS API and request short-lived credentials that will be used -to access other AWS services. - -### aws\_recipes\_init\_sts\_session.py - -The -[aws\_recipes\_init\_sts\_session.py](https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_init_sts_session.py) -tool reads long-lived credentials configured in the .aws/credentials.no-mfa -file, prompts users for their MFA code, and retrieves STS credentials (AWS -access key ID, AWS secret key, and session token). The short-lived credentials -are then saved under the standardized *.aws/credentials* file to be accessible -to the AWS CLI and other tools built with the AWS SDKs. The following code -snippet demonstrates calling this tool to request an STS session token: - - $ ./aws_recipes_init_sts_session.py --profile isecpartners - Enter your MFA code: 123456 - Successfully configured the session token for profile 'isecpartners'. - -When looking at the *.aws* folder, we can see that a standard *credentials* -file now exists as well and that it contains the short-lived credentials: - - $ ls -l ~/.aws - total 8 - -rw-r--r-- 1 loic loic 576 Apr 3 14:14 credentials - -rw-r--r-- 1 loic loic 179 Apr 3 14:00 credentials.no-mfa - $ cat ~/.aws/credentials - [isecpartners] - aws_access_key_id = STS_KEY_ID - aws_secret_access_key = STS_SECRET_KEY - aws_mfa_serial = arn:aws:iam::AWS_ACCOUNT_ID:mfa/USER_NAME - aws_session_token = AWS//////////SESSION_TOKEN - -Now that the short-lived credentials are configured, we can use the AWS CLI or -other tools built with the AWS SDKs that read credentials from this standard -location. When the STS session expires, users just need to re-run the -[aws\_recipes\_init\_sts\_session.py](https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_init_sts_session.py) -tool and the standard *credentials* file will be updated with new valid -short-lived credentials. - -### Conclusion - -By using this pair of tools to manage their AWS access keys, IAM users can -easily use the AWS CLI and other tools built with various AWS SDKs in -environments that have been secured and enforce MFA-protected API access. diff --git a/_posts/2015-06-09-iam_user_management_2.markdown b/_posts/2015-06-09-iam_user_management_2.markdown deleted file mode 100644 index 8f4cb59..0000000 --- a/_posts/2015-06-09-iam_user_management_2.markdown +++ /dev/null @@ -1,155 +0,0 @@ ---- -layout: post -title: "IAM user management strategy (part 2)" -date: 2015-06-09 09:20 -post_author: Loïc Simon -categories: AWS -tags: iSEC ---- - -The previous [IAM user management strategy] -({{{site.baseurl}}/aws/2015/02/24/iam_user_management.html) post discussed how -usage of IAM groups enables AWS administrators to consistently grant privileges -and enforce a number of security rules (such as MFA-protected API access). This -blog post will build on this idea by introducing category groups and -documenting new tools to improve IAM user management. - -### Categorize your IAM users - -For a variety of reasons, applying a single set of security rules to all IAM -users is not always practical. For example, because many applications running -in AWS predate IAM roles, numerous environments still rely on the existence of -headless IAM users. Additionally, third parties may be granted access to an AWS -account for a number of reasons but may not be able to comply with the same set -of security rules that employees follow. For this reason, NCC recommends using -category groups to sort IAM users and reliably enforce appropriate security -measures. For example, one group for all human users and a second for all headless users may be -created: MFA-protected API access and password management are not relevant for -headless users. Furthermore, human users may be categorized into several groups -such as employees and contractors: API access can be restricted to the -corporate IP range for employees but might not be achievable for contractors. - -*Note 1:* The set of category groups should define all types of IAM users that -may exist in your AWS account and each IAM user should belong to one -- and -only one -- category group (they may belong to other groups though). - -*Note 2:* The common group and category groups should be used to enable enforcing -security in one's AWS environment. Policies attached to these groups should be -carefully reviewed and grant the minimum set of privileges necessary for this -type of IAM user (*e.g.* credential management for humans). - -### Example of category groups - -The rest of this article describes a number of tools developed and used by -NCC to help implement this IAM user management strategy. These tools can be found -in the [AWS-Recipes](https://github.com/iSECPartners/AWS-recipes) repository. We will -use our test AWS environment as an example, in which we use three category groups in -addition to the *AllUsers* common group: - -1. *AllHumans*, the group all employees must belong to. -1. *AllHeadlessUsers*, the group all headless IAM users must belong to. -1. *AllMisconfiguredUsers*, a placeholder for sample misconfigured users. - -We also have an IAM user naming convention that requires usernames to match the -following schema: - -1. Employees: firstname initial appended with lastname -1. Headless user: name of the service prefixed with *HeadlessUser-* -1. Misconfigured: description of the misconfiguration prefixed with *MisconfiguredUser-* - -Based on these rules, we created a configuration file stored under -*.aws/recipes/isecpartners.json*, with *isecpartners* matching the profile's -name. If you do not use profiles, the configuration will be under -*.aws/recipes/default.json*. - - { - "common_groups": [ "AllUsers" ], - "category_groups": [ - "AllHumanUsers", - "AllHeadlessUsers", - "AllMisconfiguredUsers" - ], - "category_regex": [ - "", - "^Headless-(.*)", - "^MisconfiguredUser-(.*)" - ], - "profile_name": [ "isecpartners" ] - } - - -This configuration file declares the name of the common IAM group and two lists -related to the categorization of IAM users: - -1. A list of category groups. -1. A list of regular expressions matching our naming convention. - - -*Note 1:* If you do not have a naming convention in place to distinguish the -type of user, remove the *category_regex* attribute from your configuration -file. - -*Note 2:* If a regular expression is only applicable to a subset of category -groups, you must ensure that both lists have the same length and use an empty -string for groups that cannot be automatically associated (see the -*AllHumanUsers* group in our example). - -*Note 3:* Use of a configuration file is not necessary as all values may be -passed as command line arguments. If a configuration file exists and a value is -passed as an argument, the value passed via the command line will be used. - -### Create your default groups with *[aws\_iam\_create\_default\_groups.py](https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_iam_create_default_groups.py)* - -The purpose of this tool is to create IAM groups whose name matches the common -and category groups specified in the above configuration file. Running the -following command results in four new groups being created if they did not -already exist. - - ./aws_iam_create_default_groups.py --profile isecpartners - -### (Automatically) sort IAM users with *[aws\_iam\_sort\_users.py](https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_iam_sort_users.py).* - -This tool iterates through all IAM users and attempts to automatically detect -the IAM groups each user should belong to. For convenience, we recommend adding -the following to your AWS recipes configuration files: - - "aws_sort_users.py": { - "create_groups": false, - }, - "force_common_group": true - -This specifies default values for additional arguments to be set when running -*aws\_iam\_sort\_users.py*. Specifically, with these values, running this tool -will automatically add all IAM users to the common group *AllUsers* and will -not attempt to create the default groups (not necessary as we already did -this). Additionally, this tool checks that each IAM user belongs to one of the -category groups. If this is not the case and the username matches a regular -expression, the user is automatically added to the matching category group. Otherwise, a -multi-choice prompt appears to allow manual selection of the appropriate -category group. - -### Additional advantages of configuration files - -Besides helping with simplification of these tools' usage, this new AWS-recipe -configuration file can be used across tools, allowing for more consistent -rule enforcement. For example, the -*[aws\_iam\_create\_user.py](https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_iam_create_user.py).* -tool uses this configuration file and applies the same business logic to add -users to the common group and appropriate category group at user creation time. In -our test environment, for example, running the following command automatically -added the new user to the *MisconfiguredUser* group: - - $ ./aws_iam_create_user.py --profile isecpartners --users MisconfiguredUser-BlogPostExample - Creating user MisconfiguredUser-BlogPostExample... - Save unencrypted value (y/n)? y - User 'MisconfiguredUser-BlogPostExample' does not belong to the mandatory common group 'AllUsers'. Do you want to remediate this now (y/n)? y - User 'MisconfiguredUser-BlogPostExample' does not belong to any of the category group (AllHumanUsers, AllHeadlessUsers, AllMisconfiguredUsers). Automatically adding... - Enabling MFA for user MisconfiguredUser-BlogPostExample... - -### Conclusion - -While efficient and reliable management of IAM users can be challenging, using -the right strategy and tools significantly simplifies this process. Creation -and use of a naming convention for IAM users enables -automated user management and enforcement of security rules. - diff --git a/_posts/2015-08-03-boto3_opinel_scout_recipes.markdown b/_posts/2015-08-03-boto3_opinel_scout_recipes.markdown deleted file mode 100644 index ecd24e5..0000000 --- a/_posts/2015-08-03-boto3_opinel_scout_recipes.markdown +++ /dev/null @@ -1,86 +0,0 @@ ---- -layout: post -title: "Introducing opinel: Scout2's favorite tool" -date: 2015-08-03 11:08 -post_author: Loïc Simon -categories: AWS -tags: iSEC ---- - -With boto3 being stable and generally available[1], I took the opportunity -to migrate Scout2 and AWS-recipes to boto3. As part of that migration -effort, I decided to publish the formerly-known-as AWSUtils repository -- used -by Scout2 and AWS-recipes -- as a python package required by these tools, -rather than requiring users to work with Git submodules. I've also added more -flexibility when working with MFA-protected API calls and improved versioning -across the project. - -### opinel - -To avoid name conflicts, I decided to rename the shared AWSUtils code to a -less misleading name: opinel. The opinel package is published on [PyPI](https://pypi.python.org/pypi/opinel), and thus can -be installed using pip and easy\_install. The corresponding source code is still -open-sourced on Github at [https://github.com/iSECPartners/opinel](https://github.com/iSECPartners/opinel). -As a result, Scout2 and AWS-recipes have been modified to list opinel as a -requirement, which significantly simplifies installation and management of this -shared code. - -### Support for Python 2.7 and 3.x - -Because boto3 supports both Python2 and Python3, I decided to make sure that -the code built on top of that package has similar properties. As a result, -the latest versions of Scout2 and AWS-recipes support Python 2.7 and 3.x. -Note that opinel will **NOT** work with Python 2.6. - -### Modification of the MFA workflow - -As requested by a user of AWS-recipes[2], I modified the workflow when -using MFA-protected API access to no longer store the long-lived credentials -in a separate file. As a result, the *.aws/credentials.no-mfa* file is no -longer supported and all credentials are stored in the standard AWS credentials -file under *.aws/credentials*. Usage of the existing tools remains unchanged, -but the long-lived credentials are now accessible via a new profile name: -*profile\_name-nomfa*. This allows users to work with both STS and long-lived -credentials if need be. - -If you already had configured your environment to work with MFA-protected API -access, you will need to copy your long-lived credentials back to the -*.aws/credentials* file. This can be done with a simple command such as the -following: - - cat ~/.aws/credentials.no-mfa | sed -e 's/]$/-nomfa]/g' >> ~/.aws/credentials - -### Support to use assumed-role credentials - -With this new workflow implemented, I created a new recipe that allows -configuration of role-credentials in the *.aws/credentials* file. When the following -command is run, it uses the credentials associated with the *isecpartners* -profile to request role credentials for the IAM-Scout2 role. The role -credentials are then written in the *.aws/credentials* file in a new profile -named *isecpartners-Scout2*, which is the profile name appended by the role -session name. - - $ ./aws_recipes_assume_role.py --profile isecpartners --role-arn arn:aws:iam::AWS_ACCOUNT_ID:role/IAM-Scout2 --role-session-name Scout2 - -Users can then use their favorite tools that support profiles. For example, -Scout2 could be run with the following command line: - - $ ./Scout2.py --profile isecpartners-Scout2 - -Note that this recipe supports MFA if the assumed role requires it: - - * If you never configured your environment to work with MFA, you can provide your MFA serial number (ARN) and current token code as arguments. - * If you already configured your environment to work with MFA and stored your MFA serial in the *.aws/credentials* file, you just need to pass your token code as an additional argument. - * Finally, if you already initiated an STS session, you do not need to provide a new token code and can run the command as above. - -### Conclusion - -With the release of opinel, I hope to simplify distribution and management of -the code shared between Scout2 and AWS-recipes. Additionally, I -significantly modified the workflow and credentials storage when working with -MFA-protected API calls, which allows users to use both their long-lived and STS -credentials. - -[1]: https://aws.amazon.com/about-aws/whats-new/2015/06/boto3-aws-sdk-for-python-version-3-is-now-generally-available - -[2]: https://github.com/iSECPartners/opinel/issues/4 diff --git a/_posts/2015-08-06-scout2_supports_redshift.markdown b/_posts/2015-08-06-scout2_supports_redshift.markdown deleted file mode 100644 index a8769bf..0000000 --- a/_posts/2015-08-06-scout2_supports_redshift.markdown +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: post -title: "Redshift support added in Scout2" -date: 2015-08-06 08:39 -post_author: Loïc Simon -categories: AWS -tags: NCC ---- - -Today, I am excited to announce that support for Redshift was added in -Scout2. By default, Scout2 will fetch information about your Redshift clusters, -cluster parameter groups, and cluster security groups if you still use -EC2-Classic. At this stage, Scout2 comes with six Redshift security rules that -are enabled by default: - - * Clusters - * Check whether version upgrade is enabled - * Check whether the cluster is publicly accessible - * Check whether database encryption is enabled - * Cluster parameter groups - * Check whether SSL/TLS is required to access the database - * Check whether user activity logging is enabled - * Cluster security groups (EC2-classic) - * Check whether the security group allows access to all IP addresses (0.0.0.0/0) - -Scout2 was first released over a year and a half ago, and proved to be extremely -helpful when performing AWS configuration reviews. While -Scout2's initial release only supported three services (IAM, EC2, and S3) and -included thirteen security checks, the tool rapidly grew to add support for RDS -and CloudTrail. Furthermore, the tool now offers over fifty tests throughout -these five supported services. I hope that support for Redshift will bring -value to users of Scout2, and welcome feature requests, bug reports, and -recommendations on Github at -[https://github.com/iSECPartners/Scout2/issues](https://github.com/iSECPartners/Scout2/issues). diff --git a/_posts/2015-11-17-ip_ranges_management.markdown b/_posts/2015-11-17-ip_ranges_management.markdown deleted file mode 100644 index 1ff9818..0000000 --- a/_posts/2015-11-17-ip_ranges_management.markdown +++ /dev/null @@ -1,302 +0,0 @@ ---- -layout: post -title: "Efficient review of AWS security groups' CIDR grants" -date: 2015-11-17 08:00 -post_author: Loïc Simon -categories: AWS -tags: NCC ---- - -A significant challenge for companies using the cloud lies in ensuring that -their firewall rules follow the principle of least privilege. It is -extremely common nowadays to delegate management of security groups to developers, for -both production and test environments. This means that security groups and -their associated rules are managed by a much larger number of employees than -what used to be the case in non-cloud environments, where a unique, smaller -team was in charge of managing all firewall rules. Due to the more dynamic -nature of cloud-based infrastructures, companies should review their cloud -environment's firewall rules on a more frequent basis than for non cloud-based -systems. Unfortunately, this is a difficult exercise due to the large number of -[CIDRs](https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing) that may be whitelisted in a given AWS account. Keeping track of all -known CIDRs and what hosts or networks they represent is not easy for -employees, and is almost impossible for external auditors who must perform the -review within a limited timeframe. - -In this post, I will document how this issue can be addressed using the -[AWS-Recipes](https://github.com/iSECPartners/AWS-recipes) tools and -[Scout2](https://github.com/iSECPartners/Scout2). - -### Feed custom ip-ranges files to Scout2 - -Today, I am excited to announce that Scout2 accepts JSON files that -contain known CIDRs along with arbitrary metadata such as the host or network -they represent. When provided with such files, Scout2's report displays the -"friendly name" of each known CIDR that is whitelisted in security group rules. -This means that, instead of reviewing a list of obscure IP ranges, users of -Scout2 may now rely on the name associated with each CIDR. - -In order to use this new feature, Scout2 should be run with the following -arguments: - - ./Scout2.py --profile nccgroup --ip-ranges ip-ranges-nccgroup.json ip-ranges-ncc-offices.json --ip-ranges-key-name name - -In the above command line, Scout2 receives two ip-ranges JSON files via the -"--ip-ranges" argument: - - * ip-ranges-nccgroup.json, which contains the public IP addresses in the AWS IP space in use - * ip-ranges-ncc-offices.json, which contains the public IP addresses of several offices - -Furthermore, the "--ip-ranges-key-name" argument indicates which JSON field to -display as the "friendly name". - -The following screenshot illustrates that, in the Scout2 report, the name of -each known CIDR is displayed. When an IP which belongs to a known CIDR is -whitelisted, the name of the corresponding CIDR is used. In this example, -5.5.5.42/32 belongs to the 5.5.5.0/24 CIDR, which is associated with the "San -Francisco" office. An "Unknown CIDR" value is displayed when an unknown value -is whitelisted. - -![Screenshot: Security group rules display the name of each known CIDRs](/images/aws/awsscout2-ec2-cidr-friendlyname.png) - -The next section of this blog post documents how users can create and manage -these ip-ranges JSON files. - -### Manage known CIDRs with aws\_recipes\_create\_ip\_ranges.py - -With AWS releasing their public IP address ranges, I decided to create a tool -that allows creation and management of arbitrary IP address ranges using the -same JSON format. The tool is released on GitHub at -[https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws\_recipes\_create\_ip\_ranges.py](https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_create_ip_ranges.py) -and may be used in several scenarios: - - * Automatically create ip-ranges files based on public IP addresses in AWS (Elastic IPs and EC2 instances) - * Automatically create ip-ranges files based on IP addresses documented in a CSV file - * Manually create and manage ip-ranges files - -Each of these use cases is detailed in an example below, with detailed input, -commands, and output contents. - -***Note:*** In the commands below, the "--debug" argument is used to -output pretty-printed JSON, for documentation purposes. - -#### Automatically create ip-ranges based on public IP addresses in an AWS account - -First, this tool may be used to create an ip-ranges file that contains an AWS -account's elastic IP addresses and EC2 instances' public IP addresses. By doing -so, AWS users will be able to maintain a list of public IP addresses in the AWS -IP space that are associated with their resources. Assuming that AWS -credentials are configured under the "nccgroup" profile name, the command -below may be used: - - $ ./aws_recipes_create_ip_ranges.py --profile nccgroup --debug - - Fetching public IP information for the 'nccgroup' environment... - ...in us-east-1: EC2 instances - ...in us-east-1: Elastic IP addresses - ...in ap-northeast-1: EC2 instances - ...in ap-northeast-1: Elastic IP addresses - ...in eu-west-1: EC2 instances - ...in eu-west-1: Elastic IP addresses - ...in ap-southeast-1: EC2 instances - ...in ap-southeast-1: Elastic IP addresses - ...in ap-southeast-2: EC2 instances - ...in ap-southeast-2: Elastic IP addresses - ...in us-west-2: EC2 instances - ...in us-west-2: Elastic IP addresses - ...in us-west-1: EC2 instances - ...in us-west-1: Elastic IP addresses - ...in eu-central-1: EC2 instances - ...in eu-central-1: Elastic IP addresses - ...in sa-east-1: EC2 instances - ...in sa-east-1: Elastic IP addresses - -My test environment has one elastic IP address that is not associated -with an AWS resource, and one EC2 instance that has a non-elastic public IP. -Executing the above command results in the creation of an -"ip-ranges-nccgroup.json" file that has the following contents: - - { - "createDate": "2015-11-16-22-49-27", - "prefixes": [ - { - "instance_id": "i-11223344", - "ip_prefix": "1.1.1.1", - "is_elastic": false, - "name": "Test EC2 instance", - "region": "us-west-2" - }, - { - "instance_id": null, - "ip_prefix": "2.2.2.2", - "is_elastic": true, - "name": null, - "region": "us-west-2" - } - ] - } - - -#### Automatically create ip-ranges from CSV files - -From experience, I know that many companies maintain a list of their -public IP addresses, along with other network configuration information, in alternate formats, such as CSV. In -order to help with the conversion, the tool supports reading CIDR information -from CSV files. The tool was designed to be flexible and allow the creation of IP -ranges from any CSV file. In this blog post, I provide two examples. - -This first example demonstrates how to use the tool to build a JSON file based -on the CSV column headers. Only attributes specified on the command line will -be copied over. - -Contents of test1.csv: - - ip_prefix, discarded_value, name - 4.4.4.0/24, ncc group, NY office - # This is a comment... - 5.5.5.0/24, ncc group, Seattle office - - -Command line to convert the contents of the CSV file into JSON: - - ./aws_recipes_create_ip_ranges.py --csv-ip-ranges test1.csv --attributes ip_prefix name --profile ncc-test1 --debug - -Contents of ip-ranges-ncc-test1.json: - - { - "createDate": "2015-11-17-10-22-42", - "prefixes": [ - { - "ip_prefix": "4.4.4.0/24", - "name": " NY office" - }, - { - "ip_prefix": "5.5.5.0/24", - "name": " Seattle office" - } - ] - } - -The second example demonstrates how to use the tool to parse a CSV file with -custom column names and separate columns for the base IP and subnet mask. The -"--mappings" argument determines how columns will be mapped to the JSON -file's attributes. - -Contents of test2.csv - - Base IP, Dotted Subnet Mask, Subnet Mask, Something, Name, Something else - 3.3.3.0, 255.255.255.0, /24, Value to discard, SF Office, Other value to discard - -Command line to convert the contents of the CSV file into JSON: - - ./aws_recipes_create_ip_ranges.py --csv-ip-ranges test2.csv --attributes ip_prefix mask name --mappings 0 2 4 --profile ncc-test2 --skip-first-line --debug - -Contents of ip-ranges-ncc-test2.json - - { - "createDate": "2015-11-17-10-07-22", - "prefixes": [ - { - "ip_prefix": "3.3.3.0/24", - "name": " SF Office" - } - ] - } - - -#### Manually create and update ip-ranges - -In case CIDRs were not managed in a CSV file, the tools offers an interactive -mode that may be leveraged to manually create a JSON ip-ranges file. The -following snippet illustrates how to use the tool to interactively create new -ip-ranges JSON files: - - $ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name - - Add a new IP prefix to the ip ranges (y/n)? - y - Enter the new IP prefix: - 5.5.5.0/24 - You entered "5.5.5.0/24". Is that correct (y/n)? - y - Enter the 'name' value: - San Francisco - You entered "San Francisco". Is that correct (y/n)? - y - Add a new IP prefix to the ip ranges (y/n)? - y - Enter the new IP prefix: - 6.6.6.6/32 - You entered "6.6.6.6/32". Is that correct (y/n)? - y - Enter the 'name' value: - San Francisco - You entered "San Francisco". Is that correct (y/n)? - y - Add a new IP prefix to the ip ranges (y/n)? - n - -Contents of ip-ranges-ncc-offices.json: - - { - "createDate": "2015-11-16-22-44-38", - "prefixes": [ - { - "ip_prefix": "5.5.5.0/24", - "name": "San Francisco" - }, - { - "ip_prefix": "6.6.6.6/32", - "name": "San Francisco" - } - ] - } - -The tool can also automatically add new CIDRs to existing ip-ranges files: - - $ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name --debug - - Loading existing IP ranges from ip-ranges-ncc-offices.json - Add a new IP prefix to the ip ranges (y/n)? - y - Enter the new IP prefix: - 7.7.7.7/32 - You entered "7.7.7.7/32". Is that correct (y/n)? - y - Enter the 'name' value: - Seattle - You entered "Seattle". Is that correct (y/n)? - y - Add a new IP prefix to the ip ranges (y/n)? - n - File 'ip-ranges-ncc-offices.json' already exists. Do you want to overwrite it (y/n)? - y - - $ cat ip-ranges-ncc-offices.json - { - "createDate": "2015-11-16-22-44-38", - "prefixes": [ - { - "ip_prefix": "5.5.5.0/24", - "name": "San Francisco" - }, - { - "ip_prefix": "6.6.6.6/32", - "name": "San Francisco" - }, - { - "ip_prefix": "7.7.7.7/32", - "name": "Seattle" - } - ] - } - -### Conclusion - -This addition to Scout2 provides AWS account administrators and auditors with -an improved insight into their environment. Usage of this feature should result -in further hardened security groups because detection of unknown whitelisted -CIDRs and understanding of existing rules is significantly easier. - -I am currently working on a major rework of Scout2's reporting engine, -which will further improve reporting and allow creation of new alerts when an -unknown CIDR is whitelisted. diff --git a/_posts/2016-08-08-blackhat_slides.markdown b/_posts/2016-08-08-blackhat_slides.markdown deleted file mode 100644 index d63c14a..0000000 --- a/_posts/2016-08-08-blackhat_slides.markdown +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: post -title: "Post Black Hat US 2016 blog post" -date: 2016-08-08 23:28 -post_author: Loïc Simon -categories: AWS ---- - -Last Tuesday (August 3rd), I presented "Access Keys Will Kill You Before You -Kill The Password" at Black Hat US 2016. The summary is on the Black -Hat website and the updated slide deck is available Here. -This presentation aimed at highlighting risks associated with usage of AWS API -access keys in environments that do not enforce MFA-protected API access, and -documented strategies and IAM policies to help address these risks. - -On Wednesday (August 4th), I presented Scout2 at Black Hat Arsenal. During two -hours, I had the opportunity to demo Scout2 and meet users of the tool who -shared valuable feedback with me. I look forward to implementing some of the -features discussed during this event, including adding support for ECS and -finishing the new rules generator. - -As a reminder, Scout2 is available on Github, feedback is appreciated, -and feature requests and pull requests are welcome. The Scout2 documentation is -available at https://nccgroup.github.io/Scout2. - diff --git a/_site/feed.xml b/_site/feed.xml deleted file mode 100644 index 627e769..0000000 --- a/_site/feed.xml +++ /dev/null @@ -1,1084 +0,0 @@ - - - - l01cd3v.github.io - - http://l01cd3v.github.io - - - - Post Black Hat US 2016 blog post - <p>Last Tuesday (August 3rd), I presented &quot;Access Keys Will Kill You Before You -Kill The Password&quot; at Black Hat US 2016. The summary is on the <a -target="_blank" -href="https://www.blackhat.com/us-16/briefings.html#access-keys-will-kill-you-before-you-kill-the-password">Black -Hat website</a> and the updated slide deck is available <a -href="slides/us-16-Simon-Access-Keys-Will-Kill-You-Before-You-Kill-The-Password.pdf">Here</a>. -This presentation aimed at highlighting risks associated with usage of AWS API -access keys in environments that do not enforce MFA-protected API access, and -documented strategies and IAM policies to help address these risks.</p> - -<p>On Wednesday (August 4th), I presented Scout2 at Black Hat Arsenal. During two -hours, I had the opportunity to demo Scout2 and meet users of the tool who -shared valuable feedback with me. I look forward to implementing some of the -features discussed during this event, including adding support for ECS and -finishing the new rules generator.</p> - -<p>As a reminder, Scout2 is available on <a target="_blank" -href="https://github.com/nccgroup/Scout2">Github</a>, feedback is appreciated, -and feature requests and pull requests are welcome. The Scout2 documentation is -available at <a target="_blank" -href="https://nccgroup.github.io/Scout2">https://nccgroup.github.io/Scout2</a>.</p> - - Mon, 08 Aug 2016 23:28:00 -0700 - http://l01cd3v.github.io/aws/2016/08/08/blackhat_slides.html - http://l01cd3v.github.io/aws/2016/08/08/blackhat_slides.html - - - - Efficient review of AWS security groups' CIDR grants - <p>A significant challenge for companies using the cloud lies in ensuring that -their firewall rules follow the principle of least privilege. It is -extremely common nowadays to delegate management of security groups to developers, for -both production and test environments. This means that security groups and -their associated rules are managed by a much larger number of employees than -what used to be the case in non-cloud environments, where a unique, smaller -team was in charge of managing all firewall rules. Due to the more dynamic -nature of cloud-based infrastructures, companies should review their cloud -environment&#39;s firewall rules on a more frequent basis than for non cloud-based -systems. Unfortunately, this is a difficult exercise due to the large number of -<a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing">CIDRs</a> that may be whitelisted in a given AWS account. Keeping track of all -known CIDRs and what hosts or networks they represent is not easy for -employees, and is almost impossible for external auditors who must perform the -review within a limited timeframe.</p> - -<p>In this post, I will document how this issue can be addressed using the -<a href="https://github.com/iSECPartners/AWS-recipes">AWS-Recipes</a> tools and -<a href="https://github.com/iSECPartners/Scout2">Scout2</a>.</p> - -<h3>Feed custom ip-ranges files to Scout2</h3> - -<p>Today, I am excited to announce that Scout2 accepts JSON files that -contain known CIDRs along with arbitrary metadata such as the host or network -they represent. When provided with such files, Scout2&#39;s report displays the -&quot;friendly name&quot; of each known CIDR that is whitelisted in security group rules. -This means that, instead of reviewing a list of obscure IP ranges, users of -Scout2 may now rely on the name associated with each CIDR.</p> - -<p>In order to use this new feature, Scout2 should be run with the following -arguments:</p> -<div class="highlight"><pre><code class="language-" data-lang="">./Scout2.py --profile nccgroup --ip-ranges ip-ranges-nccgroup.json ip-ranges-ncc-offices.json --ip-ranges-key-name name -</code></pre></div> -<p>In the above command line, Scout2 receives two ip-ranges JSON files via the -&quot;--ip-ranges&quot; argument:</p> - -<ul> -<li>ip-ranges-nccgroup.json, which contains the public IP addresses in the AWS IP space in use</li> -<li>ip-ranges-ncc-offices.json, which contains the public IP addresses of several offices</li> -</ul> - -<p>Furthermore, the &quot;--ip-ranges-key-name&quot; argument indicates which JSON field to -display as the &quot;friendly name&quot;.</p> - -<p>The following screenshot illustrates that, in the Scout2 report, the name of -each known CIDR is displayed. When an IP which belongs to a known CIDR is -whitelisted, the name of the corresponding CIDR is used. In this example, -5.5.5.42/32 belongs to the 5.5.5.0/24 CIDR, which is associated with the &quot;San -Francisco&quot; office. An &quot;Unknown CIDR&quot; value is displayed when an unknown value -is whitelisted.</p> - -<p><img src="/images/aws/awsscout2-ec2-cidr-friendlyname.png" alt="Screenshot: Security group rules display the name of each known CIDRs"></p> - -<p>The next section of this blog post documents how users can create and manage -these ip-ranges JSON files.</p> - -<h3>Manage known CIDRs with aws_recipes_create_ip_ranges.py</h3> - -<p>With AWS releasing their public IP address ranges, I decided to create a tool -that allows creation and management of arbitrary IP address ranges using the -same JSON format. The tool is released on GitHub at -<a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_create_ip_ranges.py">https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_create_ip_ranges.py</a> -and may be used in several scenarios:</p> - -<ul> -<li>Automatically create ip-ranges files based on public IP addresses in AWS (Elastic IPs and EC2 instances)</li> -<li>Automatically create ip-ranges files based on IP addresses documented in a CSV file</li> -<li>Manually create and manage ip-ranges files</li> -</ul> - -<p>Each of these use cases is detailed in an example below, with detailed input, -commands, and output contents.</p> - -<p><strong><em>Note:</em></strong> In the commands below, the &quot;--debug&quot; argument is used to -output pretty-printed JSON, for documentation purposes.</p> - -<h4>Automatically create ip-ranges based on public IP addresses in an AWS account</h4> - -<p>First, this tool may be used to create an ip-ranges file that contains an AWS -account&#39;s elastic IP addresses and EC2 instances&#39; public IP addresses. By doing -so, AWS users will be able to maintain a list of public IP addresses in the AWS -IP space that are associated with their resources. Assuming that AWS -credentials are configured under the &quot;nccgroup&quot; profile name, the command -below may be used:</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_create_ip_ranges.py --profile nccgroup --debug - -Fetching public IP information for the 'nccgroup' environment... -...in us-east-1: EC2 instances -...in us-east-1: Elastic IP addresses -...in ap-northeast-1: EC2 instances -...in ap-northeast-1: Elastic IP addresses -...in eu-west-1: EC2 instances -...in eu-west-1: Elastic IP addresses -...in ap-southeast-1: EC2 instances -...in ap-southeast-1: Elastic IP addresses -...in ap-southeast-2: EC2 instances -...in ap-southeast-2: Elastic IP addresses -...in us-west-2: EC2 instances -...in us-west-2: Elastic IP addresses -...in us-west-1: EC2 instances -...in us-west-1: Elastic IP addresses -...in eu-central-1: EC2 instances -...in eu-central-1: Elastic IP addresses -...in sa-east-1: EC2 instances -...in sa-east-1: Elastic IP addresses -</code></pre></div> -<p>My test environment has one elastic IP address that is not associated -with an AWS resource, and one EC2 instance that has a non-elastic public IP. -Executing the above command results in the creation of an -&quot;ip-ranges-nccgroup.json&quot; file that has the following contents:</p> -<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> - </span><span class="nt">"createDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2015-11-16-22-49-27"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"prefixes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"instance_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"i-11223344"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.1.1.1"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"is_elastic"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> - </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Test EC2 instance"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"region"</span><span class="p">:</span><span class="w"> </span><span class="s2">"us-west-2"</span><span class="w"> - </span><span class="p">},</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"instance_id"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> - </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.2.2.2"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"is_elastic"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> - </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> - </span><span class="nt">"region"</span><span class="p">:</span><span class="w"> </span><span class="s2">"us-west-2"</span><span class="w"> - </span><span class="p">}</span><span class="w"> - </span><span class="p">]</span><span class="w"> -</span><span class="p">}</span><span class="w"> -</span></code></pre></div> -<h4>Automatically create ip-ranges from CSV files</h4> - -<p>From experience, I know that many companies maintain a list of their -public IP addresses, along with other network configuration information, in alternate formats, such as CSV. In -order to help with the conversion, the tool supports reading CIDR information -from CSV files. The tool was designed to be flexible and allow the creation of IP -ranges from any CSV file. In this blog post, I provide two examples.</p> - -<p>This first example demonstrates how to use the tool to build a JSON file based -on the CSV column headers. Only attributes specified on the command line will -be copied over.</p> - -<p>Contents of test1.csv:</p> -<div class="highlight"><pre><code class="language-" data-lang="">ip_prefix, discarded_value, name -4.4.4.0/24, ncc group, NY office -# This is a comment... -5.5.5.0/24, ncc group, Seattle office -</code></pre></div> -<p>Command line to convert the contents of the CSV file into JSON:</p> -<div class="highlight"><pre><code class="language-" data-lang="">./aws_recipes_create_ip_ranges.py --csv-ip-ranges test1.csv --attributes ip_prefix name --profile ncc-test1 --debug -</code></pre></div> -<p>Contents of ip-ranges-ncc-test1.json:</p> -<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> - </span><span class="nt">"createDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2015-11-17-10-22-42"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"prefixes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"4.4.4.0/24"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">" NY office"</span><span class="w"> - </span><span class="p">},</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5.5.5.0/24"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">" Seattle office"</span><span class="w"> - </span><span class="p">}</span><span class="w"> - </span><span class="p">]</span><span class="w"> -</span><span class="p">}</span><span class="w"> -</span></code></pre></div> -<p>The second example demonstrates how to use the tool to parse a CSV file with -custom column names and separate columns for the base IP and subnet mask. The -&quot;--mappings&quot; argument determines how columns will be mapped to the JSON -file&#39;s attributes.</p> - -<p>Contents of test2.csv</p> -<div class="highlight"><pre><code class="language-" data-lang="">Base IP, Dotted Subnet Mask, Subnet Mask, Something, Name, Something else -3.3.3.0, 255.255.255.0, /24, Value to discard, SF Office, Other value to discard -</code></pre></div> -<p>Command line to convert the contents of the CSV file into JSON:</p> -<div class="highlight"><pre><code class="language-" data-lang="">./aws_recipes_create_ip_ranges.py --csv-ip-ranges test2.csv --attributes ip_prefix mask name --mappings 0 2 4 --profile ncc-test2 --skip-first-line --debug -</code></pre></div> -<p>Contents of ip-ranges-ncc-test2.json</p> -<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> - </span><span class="nt">"createDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2015-11-17-10-07-22"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"prefixes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"3.3.3.0/24"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">" SF Office"</span><span class="w"> - </span><span class="p">}</span><span class="w"> - </span><span class="p">]</span><span class="w"> -</span><span class="p">}</span><span class="w"> -</span></code></pre></div> -<h4>Manually create and update ip-ranges</h4> - -<p>In case CIDRs were not managed in a CSV file, the tools offers an interactive -mode that may be leveraged to manually create a JSON ip-ranges file. The -following snippet illustrates how to use the tool to interactively create new -ip-ranges JSON files:</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name - -Add a new IP prefix to the ip ranges (y/n)? -y -Enter the new IP prefix: -5.5.5.0/24 -You entered "5.5.5.0/24". Is that correct (y/n)? -y -Enter the 'name' value: -San Francisco -You entered "San Francisco". Is that correct (y/n)? -y -Add a new IP prefix to the ip ranges (y/n)? -y -Enter the new IP prefix: -6.6.6.6/32 -You entered "6.6.6.6/32". Is that correct (y/n)? -y -Enter the 'name' value: -San Francisco -You entered "San Francisco". Is that correct (y/n)? -y -Add a new IP prefix to the ip ranges (y/n)? -n -</code></pre></div> -<p>Contents of ip-ranges-ncc-offices.json:</p> -<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> - </span><span class="nt">"createDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2015-11-16-22-44-38"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"prefixes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5.5.5.0/24"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"San Francisco"</span><span class="w"> - </span><span class="p">},</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6.6.6.6/32"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"San Francisco"</span><span class="w"> - </span><span class="p">}</span><span class="w"> - </span><span class="p">]</span><span class="w"> -</span><span class="p">}</span><span class="w"> -</span></code></pre></div> -<p>The tool can also automatically add new CIDRs to existing ip-ranges files:</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name --debug - -Loading existing IP ranges from ip-ranges-ncc-offices.json -Add a new IP prefix to the ip ranges (y/n)? -y -Enter the new IP prefix: -7.7.7.7/32 -You entered "7.7.7.7/32". Is that correct (y/n)? -y -Enter the 'name' value: -Seattle -You entered "Seattle". Is that correct (y/n)? -y -Add a new IP prefix to the ip ranges (y/n)? -n -File 'ip-ranges-ncc-offices.json' already exists. Do you want to overwrite it (y/n)? -y - -$ cat ip-ranges-ncc-offices.json -{ - "createDate": "2015-11-16-22-44-38", - "prefixes": [ - { - "ip_prefix": "5.5.5.0/24", - "name": "San Francisco" - }, - { - "ip_prefix": "6.6.6.6/32", - "name": "San Francisco" - }, - { - "ip_prefix": "7.7.7.7/32", - "name": "Seattle" - } - ] -} -</code></pre></div> -<h3>Conclusion</h3> - -<p>This addition to Scout2 provides AWS account administrators and auditors with -an improved insight into their environment. Usage of this feature should result -in further hardened security groups because detection of unknown whitelisted -CIDRs and understanding of existing rules is significantly easier.</p> - -<p>I am currently working on a major rework of Scout2&#39;s reporting engine, -which will further improve reporting and allow creation of new alerts when an -unknown CIDR is whitelisted.</p> - - Tue, 17 Nov 2015 08:00:00 -0800 - http://l01cd3v.github.io/aws/2015/11/17/ip_ranges_management.html - http://l01cd3v.github.io/aws/2015/11/17/ip_ranges_management.html - - - - Redshift support added in Scout2 - <p>Today, I am excited to announce that support for Redshift was added in -Scout2. By default, Scout2 will fetch information about your Redshift clusters, -cluster parameter groups, and cluster security groups if you still use -EC2-Classic. At this stage, Scout2 comes with six Redshift security rules that -are enabled by default:</p> - -<ul> -<li>Clusters - -<ul> -<li>Check whether version upgrade is enabled</li> -<li>Check whether the cluster is publicly accessible</li> -<li>Check whether database encryption is enabled</li> -</ul></li> -<li>Cluster parameter groups - -<ul> -<li>Check whether SSL/TLS is required to access the database</li> -<li>Check whether user activity logging is enabled</li> -</ul></li> -<li>Cluster security groups (EC2-classic) - -<ul> -<li>Check whether the security group allows access to all IP addresses (0.0.0.0/0)</li> -</ul></li> -</ul> - -<p>Scout2 was first released over a year and a half ago, and proved to be extremely -helpful when performing AWS configuration reviews. While -Scout2&#39;s initial release only supported three services (IAM, EC2, and S3) and -included thirteen security checks, the tool rapidly grew to add support for RDS -and CloudTrail. Furthermore, the tool now offers over fifty tests throughout -these five supported services. I hope that support for Redshift will bring -value to users of Scout2, and welcome feature requests, bug reports, and -recommendations on Github at -<a href="https://github.com/iSECPartners/Scout2/issues">https://github.com/iSECPartners/Scout2/issues</a>.</p> - - Thu, 06 Aug 2015 08:39:00 -0700 - http://l01cd3v.github.io/aws/2015/08/06/scout2_supports_redshift.html - http://l01cd3v.github.io/aws/2015/08/06/scout2_supports_redshift.html - - - - Introducing opinel: Scout2's favorite tool - <p>With boto3 being stable and generally available<sup><a href="https://aws.amazon.com/about-aws/whats-new/2015/06/boto3-aws-sdk-for-python-version-3-is-now-generally-available">1</a></sup>, I took the opportunity -to migrate Scout2 and AWS-recipes to boto3. As part of that migration -effort, I decided to publish the formerly-known-as AWSUtils repository -- used -by Scout2 and AWS-recipes -- as a python package required by these tools, -rather than requiring users to work with Git submodules. I&#39;ve also added more -flexibility when working with MFA-protected API calls and improved versioning -across the project.</p> - -<h3>opinel</h3> - -<p>To avoid name conflicts, I decided to rename the shared AWSUtils code to a -less misleading name: opinel. The opinel package is published on <a href="https://pypi.python.org/pypi/opinel">PyPI</a>, and thus can -be installed using pip and easy_install. The corresponding source code is still -open-sourced on Github at <a href="https://github.com/iSECPartners/opinel">https://github.com/iSECPartners/opinel</a>. -As a result, Scout2 and AWS-recipes have been modified to list opinel as a -requirement, which significantly simplifies installation and management of this -shared code.</p> - -<h3>Support for Python 2.7 and 3.x</h3> - -<p>Because boto3 supports both Python2 and Python3, I decided to make sure that -the code built on top of that package has similar properties. As a result, -the latest versions of Scout2 and AWS-recipes support Python 2.7 and 3.x. -Note that opinel will <strong>NOT</strong> work with Python 2.6.</p> - -<h3>Modification of the MFA workflow</h3> - -<p>As requested by a user of AWS-recipes<sup><a href="https://github.com/iSECPartners/opinel/issues/4">2</a></sup>, I modified the workflow when -using MFA-protected API access to no longer store the long-lived credentials -in a separate file. As a result, the <em>.aws/credentials.no-mfa</em> file is no -longer supported and all credentials are stored in the standard AWS credentials -file under <em>.aws/credentials</em>. Usage of the existing tools remains unchanged, -but the long-lived credentials are now accessible via a new profile name: -<em>profile_name-nomfa</em>. This allows users to work with both STS and long-lived -credentials if need be.</p> - -<p>If you already had configured your environment to work with MFA-protected API -access, you will need to copy your long-lived credentials back to the -<em>.aws/credentials</em> file. This can be done with a simple command such as the -following:</p> -<div class="highlight"><pre><code class="language-" data-lang="">cat ~/.aws/credentials.no-mfa | sed -e 's/]$/-nomfa]/g' &gt;&gt; ~/.aws/credentials -</code></pre></div> -<h3>Support to use assumed-role credentials</h3> - -<p>With this new workflow implemented, I created a new recipe that allows -configuration of role-credentials in the <em>.aws/credentials</em> file. When the following -command is run, it uses the credentials associated with the <em>isecpartners</em> -profile to request role credentials for the IAM-Scout2 role. The role -credentials are then written in the <em>.aws/credentials</em> file in a new profile -named <em>isecpartners-Scout2</em>, which is the profile name appended by the role -session name.</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_assume_role.py --profile isecpartners --role-arn arn:aws:iam::AWS_ACCOUNT_ID:role/IAM-Scout2 --role-session-name Scout2 -</code></pre></div> -<p>Users can then use their favorite tools that support profiles. For example, -Scout2 could be run with the following command line:</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ./Scout2.py --profile isecpartners-Scout2 -</code></pre></div> -<p>Note that this recipe supports MFA if the assumed role requires it:</p> - -<ul> -<li>If you never configured your environment to work with MFA, you can provide your MFA serial number (ARN) and current token code as arguments.</li> -<li>If you already configured your environment to work with MFA and stored your MFA serial in the <em>.aws/credentials</em> file, you just need to pass your token code as an additional argument.</li> -<li>Finally, if you already initiated an STS session, you do not need to provide a new token code and can run the command as above.</li> -</ul> - -<h3>Conclusion</h3> - -<p>With the release of opinel, I hope to simplify distribution and management of -the code shared between Scout2 and AWS-recipes. Additionally, I -significantly modified the workflow and credentials storage when working with -MFA-protected API calls, which allows users to use both their long-lived and STS -credentials.</p> - - Mon, 03 Aug 2015 11:08:00 -0700 - http://l01cd3v.github.io/aws/2015/08/03/boto3_opinel_scout_recipes.html - http://l01cd3v.github.io/aws/2015/08/03/boto3_opinel_scout_recipes.html - - - - IAM user management strategy (part 2) - <p>The previous <a href="/aws/2015/02/24/iam_user_management.html">IAM user management strategy</a> post discussed how -usage of IAM groups enables AWS administrators to consistently grant privileges -and enforce a number of security rules (such as MFA-protected API access). This -blog post will build on this idea by introducing category groups and -documenting new tools to improve IAM user management.</p> - -<h3>Categorize your IAM users</h3> - -<p>For a variety of reasons, applying a single set of security rules to all IAM -users is not always practical. For example, because many applications running -in AWS predate IAM roles, numerous environments still rely on the existence of -headless IAM users. Additionally, third parties may be granted access to an AWS -account for a number of reasons but may not be able to comply with the same set -of security rules that employees follow. For this reason, NCC recommends using -category groups to sort IAM users and reliably enforce appropriate security -measures. For example, one group for all human users and a second for all headless users may be -created: MFA-protected API access and password management are not relevant for -headless users. Furthermore, human users may be categorized into several groups -such as employees and contractors: API access can be restricted to the -corporate IP range for employees but might not be achievable for contractors.</p> - -<p><em>Note 1:</em> The set of category groups should define all types of IAM users that -may exist in your AWS account and each IAM user should belong to one -- and -only one -- category group (they may belong to other groups though).</p> - -<p><em>Note 2:</em> The common group and category groups should be used to enable enforcing -security in one&#39;s AWS environment. Policies attached to these groups should be -carefully reviewed and grant the minimum set of privileges necessary for this -type of IAM user (<em>e.g.</em> credential management for humans).</p> - -<h3>Example of category groups</h3> - -<p>The rest of this article describes a number of tools developed and used by -NCC to help implement this IAM user management strategy. These tools can be found -in the <a href="https://github.com/iSECPartners/AWS-recipes">AWS-Recipes</a> repository. We will -use our test AWS environment as an example, in which we use three category groups in -addition to the <em>AllUsers</em> common group:</p> - -<ol> -<li><em>AllHumans</em>, the group all employees must belong to.</li> -<li><em>AllHeadlessUsers</em>, the group all headless IAM users must belong to.</li> -<li><em>AllMisconfiguredUsers</em>, a placeholder for sample misconfigured users.</li> -</ol> - -<p>We also have an IAM user naming convention that requires usernames to match the -following schema:</p> - -<ol> -<li>Employees: firstname initial appended with lastname</li> -<li>Headless user: name of the service prefixed with <em>HeadlessUser-</em></li> -<li>Misconfigured: description of the misconfiguration prefixed with <em>MisconfiguredUser-</em></li> -</ol> - -<p>Based on these rules, we created a configuration file stored under -<em>.aws/recipes/isecpartners.json</em>, with <em>isecpartners</em> matching the profile&#39;s -name. If you do not use profiles, the configuration will be under -<em>.aws/recipes/default.json</em>.</p> -<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> - </span><span class="nt">"common_groups"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"AllUsers"</span><span class="w"> </span><span class="p">],</span><span class="w"> - </span><span class="nt">"category_groups"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="s2">"AllHumanUsers"</span><span class="p">,</span><span class="w"> - </span><span class="s2">"AllHeadlessUsers"</span><span class="p">,</span><span class="w"> - </span><span class="s2">"AllMisconfiguredUsers"</span><span class="w"> - </span><span class="p">],</span><span class="w"> - </span><span class="nt">"category_regex"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="s2">""</span><span class="p">,</span><span class="w"> - </span><span class="s2">"^Headless-(.*)"</span><span class="p">,</span><span class="w"> - </span><span class="s2">"^MisconfiguredUser-(.*)"</span><span class="w"> - </span><span class="p">],</span><span class="w"> - </span><span class="nt">"profile_name"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"isecpartners"</span><span class="w"> </span><span class="p">]</span><span class="w"> -</span><span class="p">}</span><span class="w"> -</span></code></pre></div> -<p>This configuration file declares the name of the common IAM group and two lists -related to the categorization of IAM users:</p> - -<ol> -<li>A list of category groups.</li> -<li>A list of regular expressions matching our naming convention.</li> -</ol> - -<p><em>Note 1:</em> If you do not have a naming convention in place to distinguish the -type of user, remove the <em>category_regex</em> attribute from your configuration -file.</p> - -<p><em>Note 2:</em> If a regular expression is only applicable to a subset of category -groups, you must ensure that both lists have the same length and use an empty -string for groups that cannot be automatically associated (see the -<em>AllHumanUsers</em> group in our example).</p> - -<p><em>Note 3:</em> Use of a configuration file is not necessary as all values may be -passed as command line arguments. If a configuration file exists and a value is -passed as an argument, the value passed via the command line will be used.</p> - -<h3>Create your default groups with <em><a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_iam_create_default_groups.py">aws_iam_create_default_groups.py</a></em></h3> - -<p>The purpose of this tool is to create IAM groups whose name matches the common -and category groups specified in the above configuration file. Running the -following command results in four new groups being created if they did not -already exist.</p> -<div class="highlight"><pre><code class="language-" data-lang="">./aws_iam_create_default_groups.py --profile isecpartners -</code></pre></div> -<h3>(Automatically) sort IAM users with <em><a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_iam_sort_users.py">aws_iam_sort_users.py</a>.</em></h3> - -<p>This tool iterates through all IAM users and attempts to automatically detect -the IAM groups each user should belong to. For convenience, we recommend adding -the following to your AWS recipes configuration files:</p> -<div class="highlight"><pre><code class="language-" data-lang="">"aws_sort_users.py": { - "create_groups": false, -}, -"force_common_group": true -</code></pre></div> -<p>This specifies default values for additional arguments to be set when running -<em>aws_iam_sort_users.py</em>. Specifically, with these values, running this tool -will automatically add all IAM users to the common group <em>AllUsers</em> and will -not attempt to create the default groups (not necessary as we already did -this). Additionally, this tool checks that each IAM user belongs to one of the -category groups. If this is not the case and the username matches a regular -expression, the user is automatically added to the matching category group. Otherwise, a -multi-choice prompt appears to allow manual selection of the appropriate -category group.</p> - -<h3>Additional advantages of configuration files</h3> - -<p>Besides helping with simplification of these tools&#39; usage, this new AWS-recipe -configuration file can be used across tools, allowing for more consistent -rule enforcement. For example, the -<em><a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_iam_create_user.py">aws_iam_create_user.py</a>.</em> -tool uses this configuration file and applies the same business logic to add -users to the common group and appropriate category group at user creation time. In -our test environment, for example, running the following command automatically -added the new user to the <em>MisconfiguredUser</em> group:</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_iam_create_user.py --profile isecpartners --users MisconfiguredUser-BlogPostExample -Creating user MisconfiguredUser-BlogPostExample... -Save unencrypted value (y/n)? y -User 'MisconfiguredUser-BlogPostExample' does not belong to the mandatory common group 'AllUsers'. Do you want to remediate this now (y/n)? y -User 'MisconfiguredUser-BlogPostExample' does not belong to any of the category group (AllHumanUsers, AllHeadlessUsers, AllMisconfiguredUsers). Automatically adding... -Enabling MFA for user MisconfiguredUser-BlogPostExample... -</code></pre></div> -<h3>Conclusion</h3> - -<p>While efficient and reliable management of IAM users can be challenging, using -the right strategy and tools significantly simplifies this process. Creation -and use of a naming convention for IAM users enables -automated user management and enforcement of security rules.</p> - - Tue, 09 Jun 2015 09:20:00 -0700 - http://l01cd3v.github.io/aws/2015/06/09/iam_user_management_2.html - http://l01cd3v.github.io/aws/2015/06/09/iam_user_management_2.html - - - - Work daily with enforced MFA-protected API access - <h3>AWS Security Token Service</h3> - -<p>The AWS Security Token Service (STS) is the gateway used to create sessions -when MFA-protected API access is enabled. This service allows IAM users to -retrieve short-lived credentials (<em>i.e</em> access key ID, secret access key, and -session token) in exchange for their long-lived credentials (<em>i.e.</em> AWS access -key ID and secret key) and their current authentication code. When enforcing -MFA-protected API access, as recommended in the previous <a href="/aws/2015/04/02/use_and_enforce_mfa.html">Use and enforce -Multi-Factor -Authentication</a> post, -IAM users must use these short-lived credentials to access other AWS services.</p> - -<h3>Challenges with MFA-protected API access</h3> - -<p>When MFA-protected API access is enforced, managing AWS access keys becomes -challenging because configuration files that contain these credentials must be -updated regularly. Users must also ensure that they do not lose their -long-lived credentials when modifying the configuration files to write their -short-lived credentials. In order to help with this workflow, iSEC wrote and -released several simple tools in the -<a href="https://github.com/iSECPartners/AWS-recipes">AWS-recipes</a> repository.</p> - -<p>The collection of tools that we will discussed below uses the &quot;<a -target="_blank" -href="https://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs">new -and standardized way to manage credentials in the AWS SDKs</a>&quot;, meaning that -SDKs are expecting to read credentials from the <em>.aws/credentials</em> file under -the user&#39;s home or profile directory.</p> - -<h3>aws_recipes_configure_iam.py</h3> - -<p>The -<a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_configure_iam.py">aws_recipes_configure_iam.py</a> -tool allows users to configure and store their long-lived credentials in a new, -non-standard, <em>.aws/credentials.no-mfa</em> file. In addition to prompting for the -AWS access key ID and secret key, this tool also prompts for the MFA device -serial number because this information must be provided when making calls to -the STS API. Similar to the AWS CLI and SDKs, it supports profile names. The -following code snippet is an example of calling this tool to configure a new -profile called <em>isecpartners</em>:</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_configure_iam.py --profile isecpartners -AWS Access Key ID: AWS_KEY_ID -AWS Secret Access Key: AWS_SECRET_KEY -AWS MFA serial: arn:aws:iam::AWS_ACCOUNT_ID:mfa/USER_NAME -</code></pre></div> -<p>When looking at the <em>.aws</em> folder, we can see that a <em>credentials.no-mfa</em> file -exists and that it contains the credentials that were just entered:</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ls -l ~/.aws -total 4 --rw-r--r-- 1 loic loic 93 Apr 3 14:00 credentials.no-mfa -$ cat ~/.aws/credentials.no-mfa -[isecpartners] -aws_access_key_id = AWS_KEY_ID -aws_secret_access_key = AWS_SECRET_KEY -aws_mfa_serial = arn:aws:iam::AWS_ACCOUNT_ID:mfa/USER_NAME -</code></pre></div> -<p>Now that long-lived credentials are configured, we can use the next tool to -call the AWS STS API and request short-lived credentials that will be used -to access other AWS services.</p> - -<h3>aws_recipes_init_sts_session.py</h3> - -<p>The -<a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_init_sts_session.py">aws_recipes_init_sts_session.py</a> -tool reads long-lived credentials configured in the .aws/credentials.no-mfa -file, prompts users for their MFA code, and retrieves STS credentials (AWS -access key ID, AWS secret key, and session token). The short-lived credentials -are then saved under the standardized <em>.aws/credentials</em> file to be accessible -to the AWS CLI and other tools built with the AWS SDKs. The following code -snippet demonstrates calling this tool to request an STS session token:</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_init_sts_session.py --profile isecpartners -Enter your MFA code: 123456 -Successfully configured the session token for profile 'isecpartners'. -</code></pre></div> -<p>When looking at the <em>.aws</em> folder, we can see that a standard <em>credentials</em> -file now exists as well and that it contains the short-lived credentials:</p> -<div class="highlight"><pre><code class="language-" data-lang="">$ ls -l ~/.aws -total 8 --rw-r--r-- 1 loic loic 576 Apr 3 14:14 credentials --rw-r--r-- 1 loic loic 179 Apr 3 14:00 credentials.no-mfa -$ cat ~/.aws/credentials -[isecpartners] -aws_access_key_id = STS_KEY_ID -aws_secret_access_key = STS_SECRET_KEY -aws_mfa_serial = arn:aws:iam::AWS_ACCOUNT_ID:mfa/USER_NAME -aws_session_token = AWS//////////SESSION_TOKEN -</code></pre></div> -<p>Now that the short-lived credentials are configured, we can use the AWS CLI or -other tools built with the AWS SDKs that read credentials from this standard -location. When the STS session expires, users just need to re-run the -<a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_init_sts_session.py">aws_recipes_init_sts_session.py</a> -tool and the standard <em>credentials</em> file will be updated with new valid -short-lived credentials.</p> - -<h3>Conclusion</h3> - -<p>By using this pair of tools to manage their AWS access keys, IAM users can -easily use the AWS CLI and other tools built with various AWS SDKs in -environments that have been secured and enforce MFA-protected API access.</p> - - Fri, 03 Apr 2015 14:10:00 -0700 - http://l01cd3v.github.io/aws/2015/04/03/session_management_tools.html - http://l01cd3v.github.io/aws/2015/04/03/session_management_tools.html - - - - Use and enforce Multi-Factor Authentication - <h3>What is Multi-Factor Authentication?</h3> - -<p>When enabled, Multi-Factor Authentication (MFA) provides strong -defense-in-depth against compromises of credentials. MFA-enabled users have a -device that periodically generates a new authentication code (<em>i.e.</em> one-time -password); they need to enter the current authentication code along with their -static credentials (<em>i.e.</em> username and password) in order to successfully -authenticate. In addition to supporting MFA when accessing the web console -(<em>i.e.</em> password-based authentication), AWS also offers MFA-protected API -access for users who work with AWS access keys. Through the Security Token -Service (STS), IAM users can request temporary credentials in exchange for -their long-lived credentials (<em>i.e.</em> AWS access key ID and secret key) and -their current authentication code.</p> - -<h3>Why should one use and enforce MFA?</h3> - -<p>For companies deploying their application in the cloud, a breach that results -in unauthorized access to the management console &mdash; or API &mdash; is the -worst-case scenario. While a number of AWS administrators have realized the -importance of enabling MFA when they access the web console, a limited number -of them enforce MFA-protected API access. This represents a huge gap in one&#39;s -security posture because AWS access keys do not come with as many security -features as passwords do:</p> - -<ul> -<li>AWS administrators can enforce password expiration; this is currently not -possible for AWS access keys.</li> -<li>While it is probably safe to assume that most AWS administrators do not store -their password in plaintext, most of them use AWS access keys. By design, these -keys are meant to be stored in plaintext files that are accessed by tools built -with the various AWS SDKs.</li> -<li>A lost password is a forgotten password; a lost key is a key stored in a lost -file, which may be on an unencrypted storage device (e.g. hard drive or USB Flash -drive).</li> -</ul> - -<p>Because AWS access keys are long-lived credentials that are stored in plaintext -files, they are more susceptible to compromise than passwords. It is therefore -necessary to enable MFA when the AWS API is accessed using these keys and not -only when users sign in using their passwords.</p> - -<h3>How can one enforce MFA?</h3> - -<p>Unfortunately, at time of writing, AWS does not offer an option to enforce -MFA-protected API access via a global setting. Therefore, AWS account -administrators must carefully manage their IAM users and develop a strategy to -reliably achieve this. In order to enforce MFA-protected API access, iSEC -recommends the following:</p> - -<ol> -<li>Create a common IAM group that all IAM users belong to, as discussed in the previous <a href="/aws/2015/02/24/iam_user_management.html">IAM user management strategy</a> post.</li> -<li>Add the following policy (also available on <a href="https://github.com/iSECPartners/AWS-recipes/blob/master/IAM-Policies/EnforceMFA-8HourSession.json">Github</a>) to enforce MFA for all users who belong to this group.</li> -</ol> - -<p>This policy will enforce MFA regardless of how the IAM user authenticated with -AWS; it will be effective whether they use password-based or key-based -authentication.</p> -<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> - </span><span class="nt">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2012-10-17"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Statement"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Deny"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Condition"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> - </span><span class="nt">"Null"</span><span class="p">:{</span><span class="nt">"aws:MultiFactorAuthAge"</span><span class="p">:</span><span class="s2">"true"</span><span class="p">}</span><span class="w"> - </span><span class="p">}</span><span class="w"> - </span><span class="p">},</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Deny"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Condition"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> - </span><span class="nt">"NumericGreaterThan"</span><span class="p">:{</span><span class="nt">"aws:MultiFactorAuthAge"</span><span class="p">:</span><span class="s2">"28800"</span><span class="p">}</span><span class="w"> - </span><span class="p">}</span><span class="w"> - </span><span class="p">}</span><span class="w"> - </span><span class="p">]</span><span class="w"> -</span><span class="p">}</span><span class="w"> -</span></code></pre></div> -<p>The first statement in the above policy denies all actions if the -<em>aws:MultiFactorAuthAge</em> key is not present; this key only exists if MFA is -used [1].</p> - -<p>The second statement verifies that the validation of the MFA code was performed -less than eight hours ago. Temporary credentials may be valid for a duration -between fifteen minutes and thirty-six hours [2]. iSEC recommends requiring -users to initiate a new session at least once a day.</p> - -<p><strong><em>Note:</em></strong> An &quot;explicit deny&quot; means that, regardless of other policies granted -to a user, this deny rule will prevail. More information about the IAM policy -evaluation logic can be found in the AWS documentation at <a -target="_blank" -href="http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_EvaluationLogic.html">http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_EvaluationLogic.html</a>.</p> - -<h3>Use AWS Scout2 to detect users without MFA</h3> - -<p>The default ruleset used by <a href="https://github.com/iSECPartners/Scout2">AWS Scout2</a> includes a rule that checks for IAM users who have password-based authentication enabled but do not have an MFA device configured. If Scout2 detects IAM users with password-based authentication enabled and no MFA device, it will document a &quot;Lack of MFA&quot; security risk in the IAM menu dropdown, as illustrated in the below screenshot.</p> - -<p><img src="/images/aws/awsscout2-user-nomfa-1.png" alt="Screenshot: IAM menu dropdown with a &quot;Lack of MFA&quot; security risk"></p> - -<p>When clicked, this &quot;Lack of MFA&quot; link filters the list of IAM users to display -those who have password-based authentication enabled but no MFA device -configured. The red &quot;No&quot; following &quot;Multi-Factor enabled&quot; indicates a danger -tied to that particular IAM user.</p> - -<p><img src="/images/aws/awsscout2-user-nomfa-2.png" alt="Screenshot: Red &quot;No&quot; indicating that this IAM user may access the web console without MFA"></p> - -<h3>How can one use MFA with command line tools?</h3> - -<p>Users of the AWS CLI (and other command line tools) have several methods to -configure their credentials, such as environment variables, configuration -files, or command line arguments. However, updating these settings on a daily -basis when MFA-protected API access is enabled is inconvenient. To help -facilitate this work flow, iSEC has created a set of Python tools and released -them in the <a href="https://github.com/iSECPartners/AWS-recipes">AWS-recipes</a> -repository. Further details about these tools will be published in the next -blog post.</p> - -<p>Additional information about MFA with AWS is available in the AWS -documentation at <a target="_blank" -href="https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_ManagingMFA.html"> -https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_ManagingMFA.html</a>.</p> - -<h3>Conclusion</h3> - -<p>Enforcing Multi-Factor Authentication for all IAM users is extremely important -in order to mitigate the risks of credentials compromise (especially the AWS -access key ID and secret). This aspect of security is commonly overlooked and -may result in catastrophic damages. By using a strict strategy for management -of IAM users and the above IAM policy, AWS administrators may significantly -reduce risks of account compromise.</p> - -<p>[1] <a target="_blank" href="http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#AvailableKeys">http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#AvailableKeys</a></p> - -<p>[2] <a target="_blank" href="http://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html">http://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html</a></p> - - Thu, 02 Apr 2015 14:10:00 -0700 - http://l01cd3v.github.io/aws/2015/04/02/use_and_enforce_mfa.html - http://l01cd3v.github.io/aws/2015/04/02/use_and_enforce_mfa.html - - - - IAM user management strategy - <h3>Use IAM groups</h3> - -<p>When granting privileges to IAM users, AWS account administrators should avoid -use of user-specific policies. Instead, create groups whose name explicitly -defines the members&#39; job functions or responsibilities (<em>e.g.</em> AWS -Administrators, Operations, Developers, Accountants), and define the -permissions granted within group policies. Doing so will simplify the -permissions management process as changes in group policies apply to all -members.</p> - -<p>When performing AWS configuration reviews, iSEC often discovers IAM users -whose privileges have been granted via a combination of IAM user and IAM group -policies. It is not uncommon to see IAM users who are granted full -administrator privileges in a redundant manner, via both user and group -policies. Such configuration creates an avenue for configuration mistakes, as -another administrator may believe that terminating an IAM user&#39;s membership to -the admin group is sufficient. Therefore, banning use of IAM user policies -will result in making one&#39;s AWS environment less error-prone.</p> - -<p><strong><em>Note</em></strong>: It is on purpose that iSEC recommends using IAM group names that -reflect a job title or responsibility. IAM users who do not fit in such groups -(<em>e.g.</em> headless users) should not exist. Instead, AWS account administrators -should investigate use of IAM roles for EC2. Further details will be discussed -in an upcoming blog post.</p> - -<h3>Create a common IAM group to apply generic policies</h3> - -<p>Because a number of policies must be applied to all users, iSEC recommends that -AWS account administrators create an IAM group that all IAM users belong to. -Doing so will allow AWS account administrators to consistently grant privileges -and enforce a number of rules.</p> - -<p><strong><em>Note</em></strong>: It is important that all IAM users belong to this common IAM group -to ensure that policies are consistently applied. Failure to do so will create -gaps in one&#39;s AWS environment security posture.</p> - -<h3>Authorize IAM users to manage their credentials</h3> - -<p>To begin with, iSEC recommends that AWS account administrators allow all of -their IAM users to manage their credentials, and only theirs. With all IAM -users belonging to the common IAM group, this can be achieved by applying the -following IAM policy to the group.</p> -<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> - </span><span class="nt">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2012-10-17"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Statement"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="s2">"iam:*AccessKey*"</span><span class="p">,</span><span class="w"> - </span><span class="s2">"iam:*Password"</span><span class="p">,</span><span class="w"> - </span><span class="s2">"iam:*MFADevice*"</span><span class="p">,</span><span class="w"> - </span><span class="s2">"iam:UpdateLoginProfile"</span><span class="w"> - </span><span class="p">],</span><span class="w"> - </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"arn:aws:iam::AWS_ACCOUNT_ID:user/${aws:username}"</span><span class="w"> - </span><span class="p">},</span><span class="w"> - </span><span class="p">{</span><span class="w"> - </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> - </span><span class="s2">"iam:CreateVirtualMFADevice"</span><span class="p">,</span><span class="w"> - </span><span class="s2">"iam:DeleteVirtualMFADevice"</span><span class="w"> - </span><span class="p">],</span><span class="w"> - </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"arn:aws:iam::AWS_ACCOUNT_ID:mfa/${aws:username}"</span><span class="w"> - </span><span class="p">}</span><span class="w"> - </span><span class="p">]</span><span class="w"> -</span><span class="p">}</span><span class="w"> -</span></code></pre></div> -<p>While the above policy is sufficient to allow users to manage their -credentials, AWS administrators may consider the following statement as an -addition; it allows IAM users to know what the account&#39;s password policy is.</p> -<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> - </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"iam:GetAccountPasswordPolicy"</span><span class="p">,</span><span class="w"> - </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w"> -</span><span class="p">}</span><span class="w"> -</span></code></pre></div> -<h3>Use AWS Scout2 to detect user policies</h3> - -<p>The default ruleset used by <a href="https://github.com/iSECPartners/Scout2">AWS -Scout2</a> includes a rule that checks for -user policies and reports the use of user-specific IAM policies as a warning. -Detection of user-specific IAM policies results in the IAM menu dropdown -containing a &quot;User policies&quot; security risk, as illustrated in the below screenshot.</p> - -<p><img src="/images/aws/awsscout2-iam-user-policy-1.png" alt="Screenshot: IAM menu dropdown with a User policies security risk"></p> - -<p>When clicked-on, this &quot;User policies&quot; link filters the list of IAM users to only -display those who have at least one user policy attached. The orange badge -indicates a warning and the count of user policies attached to this particular -IAM user.</p> - -<p><img src="/images/aws/awsscout2-iam-user-policy-2.png" alt="Screenshot: Orange badge indicating that at least one user policy is attached to that IAM user"></p> - -<h3>Check that all IAM users belong to the common group</h3> - -<p>AWS Scout2 comes with a tool — RulesGenerator.py — that allows AWS account -administrators to generate a custom ruleset to tailor the report to their -needs. An optional IAM rule requires all IAM users to belong to a common IAM -group. In order to enable this rule, the following can be done:</p> - -<ol> -<li>Run the rules generator with the following command line: -<pre style="margin-left: -20px; margin-top: 10px; margin-bottom: 10px"><code>./RulesGenerator.py --ruleset_name isec --services iam</pre></code></li> -<li>Answer &quot;yes&quot; to the question &quot;Would you like to ensure that all IAM users belong to a given IAM group?&quot;</li> -<li>Enter the name of your common group (<em>e.g.</em> AllUsers)</li> -<li>Enter &quot;yes&quot; or &quot;y&quot; to confirm</li> -<li>Change the level if desired</li> -<li>Run Scout2</li> -</ol> - -<p><strong><em>Note</em></strong>: If you have already run Scout2 and do not wish to download the latest -IAM configuration, use the following command to run an offline analysis:</p> -<div class="highlight"><pre><code class="language-" data-lang="">./Scout2.py --ruleset_name isec --services iam --local -</code></pre></div> -<p>The following screenshot illustrates the IAM menu dropdown containing a -security risk when IAM users do not belong to the configured common group.</p> - -<p><img src="/images/aws/awsscout2-iam-user-commongroup-1.png" alt="Screenshot: IAM menu dropdown when IAM users do not belong to the common group"></p> - -<p>When clicked-on, this link filters the list of IAM users to only display those -who do not belong to the common IAM group. A colored warning sign appears, -warning about this issue.</p> - -<p><img src="/images/aws/awsscout2-iam-user-commongroup-2.png" alt="Screenshot: Orange badge indicating that at least one user policy is attached to that IAM user"></p> - -<h3>Conclusion</h3> - -<p>Strict management of IAM users and tight control of their privileges is key in -maintaining a secure AWS environment. When followed, the above recommendations -should enable AWS administrators to manage IAM users with improved efficiency -and lower the chances of overly privileged users to exist.</p> - - Tue, 24 Feb 2015 20:49:00 -0800 - http://l01cd3v.github.io/aws/2015/02/24/iam_user_management.html - http://l01cd3v.github.io/aws/2015/02/24/iam_user_management.html - - - - Do not use your AWS root account - <h3>What is the AWS root account?</h3> - -<p>The AWS root account is the account that was used -- or created -- when signing -up with Amazon Web Services. This account has full access to all resources in -the account and it is not possible to alter this configuration.</p> - -<h3>Risks of using the AWS root account</h3> - -<p>Using the AWS root account means that there is potential for its compromise. -In particular, iSEC noticed that AWS customers who use the AWS root account -tend to do the following:</p> - -<ol> -<li>Share credentials between employees.</li> -<li>Disable Multi-Factor Authentication (MFA) for convenience.</li> -</ol> - -<p>Shared credentials, aside from increasing the risk of compromise during the -sharing process, render credential rotation impractical due to the need for the -newly-generated secret to be known by multiple parties. Sharing the AWS root -account also undermines any effort towards using IAM and leveraging the -fine-grained access controls it offers. Finally, shared credentials result in -loss of the attribution ability, which makes auditing harder and may prevent -successful investigation.</p> - -<h3>AWS Identity and Access Management (IAM)</h3> - -<p>AWS IAM allows account administrators to create users for every employee and -grant them access to a limited set of services, actions, and resources. This -allows AWS account administrators to apply the principle of least privilege, -which dictates that a given user should only be able to access the information -and resources that are necessary for them to perform tasks they are responsible -for. Additionally, use of IAM allows AWS users to rotate credentials and revoke -privileges without impacting other employees.</p> - -<p>AWS account administrators should create an <em>Administrator</em> IAM group, grant -administrator privileges to this group, and create individual IAM users for -each employee in charge of administrating the AWS account. When done, the AWS -root password should be rotated and stored in a safe manner. Furthermore, -additional credentials such as access keys and certificates should be deleted.</p> - -<h3>Important security consideration about the root account</h3> - -<p>AWS users should always enable MFA on their root account, even when the -password is securely stored; it is important to realize that the password reset -for the root account process only requires access to the email address -associated with this account. <strong>This means that, without MFA, your production -environment is only as secure as an email.</strong></p> - - Mon, 23 Feb 2015 08:42:00 -0800 - http://l01cd3v.github.io/aws/2015/02/23/do_not_use_your_root_account.html - http://l01cd3v.github.io/aws/2015/02/23/do_not_use_your_root_account.html - - - - Announcing the AWS blog post series - <p>Starting this month, iSEC Partners will start a series of blog posts related to -AWS. The goal of these blog posts will be to:</p> - -<ul> -<li>Discuss common security gaps in AWS environments</li> -<li>Discuss common security gaps in the architecture of applications deployed in -the cloud</li> -<li>Describe methods and tools used to identify these security gaps</li> -<li>Share tools and scripts that facilitate daily and secure work with AWS</li> -<li>Share AWS policies that help improve the security posture of AWS environments</li> -</ul> - -<p>To share material, iSEC created a new public -<a target="_blank" -href="https://github.com/iSECPartners/AWS-recipes">AWS-recipes</a> repository -on Github. The tools and policies shared in this repository will be discussed -and explained in dedicated blog articles.</p> - -<p>Because iSEC has been assessing the security of AWS environment for several -years, we have a number of ideas and articles in the pipe awaiting to be -written and published. Our target goal is to publish at least on a bi-monthly -basis at the beginning of the project, and adjust this publication rate after -we catch up.</p> - -<p>Without further due, we will start this series with articles that discuss -Identity and Access Management (IAM) common issues and best practices, and will -present a strategy to improve one&#39;s security posture when using AWS.</p> - - Sun, 22 Feb 2015 22:24:00 -0800 - http://l01cd3v.github.io/aws/2015/02/22/announcing_the_aws_blog_post_series.html - http://l01cd3v.github.io/aws/2015/02/22/announcing_the_aws_blog_post_series.html - - - - diff --git a/_site/images/aws/awsscout2-ec2-cidr-friendlyname.png b/_site/images/aws/awsscout2-ec2-cidr-friendlyname.png deleted file mode 100755 index 7543b71..0000000 Binary files a/_site/images/aws/awsscout2-ec2-cidr-friendlyname.png and /dev/null differ diff --git a/_site/images/aws/awsscout2-iam-user-commongroup-1.png b/_site/images/aws/awsscout2-iam-user-commongroup-1.png deleted file mode 100755 index 89d5130..0000000 Binary files a/_site/images/aws/awsscout2-iam-user-commongroup-1.png and /dev/null differ diff --git a/_site/images/aws/awsscout2-iam-user-commongroup-2.png b/_site/images/aws/awsscout2-iam-user-commongroup-2.png deleted file mode 100755 index b56ebca..0000000 Binary files a/_site/images/aws/awsscout2-iam-user-commongroup-2.png and /dev/null differ diff --git a/_site/images/aws/awsscout2-iam-user-policy-1.png b/_site/images/aws/awsscout2-iam-user-policy-1.png deleted file mode 100755 index 4580050..0000000 Binary files a/_site/images/aws/awsscout2-iam-user-policy-1.png and /dev/null differ diff --git a/_site/images/aws/awsscout2-iam-user-policy-2.png b/_site/images/aws/awsscout2-iam-user-policy-2.png deleted file mode 100755 index df519b1..0000000 Binary files a/_site/images/aws/awsscout2-iam-user-policy-2.png and /dev/null differ diff --git a/_site/images/aws/awsscout2-user-nomfa-1.png b/_site/images/aws/awsscout2-user-nomfa-1.png deleted file mode 100755 index 74c0ac8..0000000 Binary files a/_site/images/aws/awsscout2-user-nomfa-1.png and /dev/null differ diff --git a/_site/images/aws/awsscout2-user-nomfa-2.png b/_site/images/aws/awsscout2-user-nomfa-2.png deleted file mode 100755 index df37040..0000000 Binary files a/_site/images/aws/awsscout2-user-nomfa-2.png and /dev/null differ diff --git a/_site/images/body-bg.png b/_site/images/body-bg.png deleted file mode 100644 index 5e8c4c2..0000000 Binary files a/_site/images/body-bg.png and /dev/null differ diff --git a/_site/images/highlight-bg.jpg b/_site/images/highlight-bg.jpg deleted file mode 100644 index 355e089..0000000 Binary files a/_site/images/highlight-bg.jpg and /dev/null differ diff --git a/_site/images/hr.png b/_site/images/hr.png deleted file mode 100644 index d32f689..0000000 Binary files a/_site/images/hr.png and /dev/null differ diff --git a/_site/index.html b/_site/index.html deleted file mode 100644 index 56bdb54..0000000 --- a/_site/index.html +++ /dev/null @@ -1,845 +0,0 @@ - - - - - - - - - - - - - - - - l01cd3v.github.io - - - -
-
-
-

l01cd3v.github.io

-

by Loïc

- -
-
-
- -
- - - -
- - - -

- - Post Black Hat US 2016 blog post - -

- -
- Published on August 8, 2016 -
- - - - - -
- -

Last Tuesday (August 3rd), I presented "Access Keys Will Kill You Before You -Kill The Password" at Black Hat US 2016. The summary is on the Black -Hat website and the updated slide deck is available Here. -This presentation aimed at highlighting risks associated with usage of AWS API -access keys in environments that do not enforce MFA-protected API access, and -documented strategies and IAM policies to help address these risks.

- -

On Wednesday (August 4th), I presented Scout2 at Black Hat Arsenal. During two -hours, I had the opportunity to demo Scout2 and meet users of the tool who -shared valuable feedback with me. I look forward to implementing some of the -features discussed during this event, including adding support for ECS and -finishing the new rules generator.

- -

As a reminder, Scout2 is available on Github, feedback is appreciated, -and feature requests and pull requests are welcome. The Scout2 documentation is -available at https://nccgroup.github.io/Scout2.

- - -
- -
- - - -
- - - - -
- - - -

- - Efficient review of AWS security groups' CIDR grants - -

- -
- Published on November 17, 2015 -
- - - - -
- [Originally published on NCC Group's blog] -
- - - -
- -

A significant challenge for companies using the cloud lies in ensuring that -their firewall rules follow the principle of least privilege. It is -extremely common nowadays to delegate management of security groups to developers, for -both production and test environments. This means that security groups and -their associated rules are managed by a much larger number of employees than -what used to be the case in non-cloud environments, where a unique, smaller -team was in charge of managing all firewall rules. Due to the more dynamic -nature of cloud-based infrastructures, companies should review their cloud -environment's firewall rules on a more frequent basis than for non cloud-based -systems. Unfortunately, this is a difficult exercise due to the large number of -CIDRs that may be whitelisted in a given AWS account. Keeping track of all -known CIDRs and what hosts or networks they represent is not easy for -employees, and is almost impossible for external auditors who must perform the -review within a limited timeframe.

- -

In this post, I will document how this issue can be addressed using the -AWS-Recipes tools and -Scout2.

- -

Feed custom ip-ranges files to Scout2

- -

Today, I am excited to announce that Scout2 accepts JSON files that -contain known CIDRs along with arbitrary metadata such as the host or network -they represent. When provided with such files, Scout2's report displays the -"friendly name" of each known CIDR that is whitelisted in security group rules. -This means that, instead of reviewing a list of obscure IP ranges, users of -Scout2 may now rely on the name associated with each CIDR.

- -

In order to use this new feature, Scout2 should be run with the following -arguments:

-
./Scout2.py --profile nccgroup --ip-ranges ip-ranges-nccgroup.json ip-ranges-ncc-offices.json --ip-ranges-key-name name
-
-

In the above command line, Scout2 receives two ip-ranges JSON files via the -"--ip-ranges" argument:

- -
    -
  • ip-ranges-nccgroup.json, which contains the public IP addresses in the AWS IP space in use
  • -
  • ip-ranges-ncc-offices.json, which contains the public IP addresses of several offices
  • -
- -

Furthermore, the "--ip-ranges-key-name" argument indicates which JSON field to -display as the "friendly name".

- -

The following screenshot illustrates that, in the Scout2 report, the name of -each known CIDR is displayed. When an IP which belongs to a known CIDR is -whitelisted, the name of the corresponding CIDR is used. In this example, -5.5.5.42/32 belongs to the 5.5.5.0/24 CIDR, which is associated with the "San -Francisco" office. An "Unknown CIDR" value is displayed when an unknown value -is whitelisted.

- -

Screenshot: Security group rules display the name of each known CIDRs

- -

The next section of this blog post documents how users can create and manage -these ip-ranges JSON files.

- -

Manage known CIDRs with aws_recipes_create_ip_ranges.py

- -

With AWS releasing their public IP address ranges, I decided to create a tool -that allows creation and management of arbitrary IP address ranges using the -same JSON format. The tool is released on GitHub at -https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_create_ip_ranges.py -and may be used in several scenarios:

- -
    -
  • Automatically create ip-ranges files based on public IP addresses in AWS (Elastic IPs and EC2 instances)
  • -
  • Automatically create ip-ranges files based on IP addresses documented in a CSV file
  • -
  • Manually create and manage ip-ranges files
  • -
- -

Each of these use cases is detailed in an example below, with detailed input, -commands, and output contents.

- -

Note: In the commands below, the "--debug" argument is used to -output pretty-printed JSON, for documentation purposes.

- -

Automatically create ip-ranges based on public IP addresses in an AWS account

- -

First, this tool may be used to create an ip-ranges file that contains an AWS -account's elastic IP addresses and EC2 instances' public IP addresses. By doing -so, AWS users will be able to maintain a list of public IP addresses in the AWS -IP space that are associated with their resources. Assuming that AWS -credentials are configured under the "nccgroup" profile name, the command -below may be used:

-
$ ./aws_recipes_create_ip_ranges.py --profile nccgroup --debug
-
-Fetching public IP information for the 'nccgroup' environment...
-...in us-east-1: EC2 instances
-...in us-east-1: Elastic IP addresses
-...in ap-northeast-1: EC2 instances
-...in ap-northeast-1: Elastic IP addresses
-...in eu-west-1: EC2 instances
-...in eu-west-1: Elastic IP addresses
-...in ap-southeast-1: EC2 instances
-...in ap-southeast-1: Elastic IP addresses
-...in ap-southeast-2: EC2 instances
-...in ap-southeast-2: Elastic IP addresses
-...in us-west-2: EC2 instances
-...in us-west-2: Elastic IP addresses
-...in us-west-1: EC2 instances
-...in us-west-1: Elastic IP addresses
-...in eu-central-1: EC2 instances
-...in eu-central-1: Elastic IP addresses
-...in sa-east-1: EC2 instances
-...in sa-east-1: Elastic IP addresses
-
-

My test environment has one elastic IP address that is not associated -with an AWS resource, and one EC2 instance that has a non-elastic public IP. -Executing the above command results in the creation of an -"ip-ranges-nccgroup.json" file that has the following contents:

-
{
-    "createDate": "2015-11-16-22-49-27",
-    "prefixes": [
-        {
-            "instance_id": "i-11223344",
-            "ip_prefix": "1.1.1.1",
-            "is_elastic": false,
-            "name": "Test EC2 instance",
-            "region": "us-west-2"
-        },
-        {
-            "instance_id": null,
-            "ip_prefix": "2.2.2.2",
-            "is_elastic": true,
-            "name": null,
-            "region": "us-west-2"
-        }
-    ]
-}
-
-

Automatically create ip-ranges from CSV files

- -

From experience, I know that many companies maintain a list of their -public IP addresses, along with other network configuration information, in alternate formats, such as CSV. In -order to help with the conversion, the tool supports reading CIDR information -from CSV files. The tool was designed to be flexible and allow the creation of IP -ranges from any CSV file. In this blog post, I provide two examples.

- -

This first example demonstrates how to use the tool to build a JSON file based -on the CSV column headers. Only attributes specified on the command line will -be copied over.

- -

Contents of test1.csv:

-
ip_prefix, discarded_value, name
-4.4.4.0/24, ncc group, NY office
-# This is a comment...
-5.5.5.0/24, ncc group, Seattle office
-
-

Command line to convert the contents of the CSV file into JSON:

-
./aws_recipes_create_ip_ranges.py --csv-ip-ranges test1.csv --attributes ip_prefix name --profile ncc-test1 --debug
-
-

Contents of ip-ranges-ncc-test1.json:

-
{
-    "createDate": "2015-11-17-10-22-42",
-    "prefixes": [
-        {
-            "ip_prefix": "4.4.4.0/24",
-            "name": " NY office"
-        },
-        {
-            "ip_prefix": "5.5.5.0/24",
-            "name": " Seattle office"
-        }
-    ]
-}
-
-

The second example demonstrates how to use the tool to parse a CSV file with -custom column names and separate columns for the base IP and subnet mask. The -"--mappings" argument determines how columns will be mapped to the JSON -file's attributes.

- -

Contents of test2.csv

-
Base IP, Dotted Subnet Mask, Subnet Mask, Something, Name, Something else
-3.3.3.0, 255.255.255.0, /24, Value to discard, SF Office, Other value to discard
-
-

Command line to convert the contents of the CSV file into JSON:

-
./aws_recipes_create_ip_ranges.py --csv-ip-ranges test2.csv --attributes ip_prefix mask name --mappings 0 2 4 --profile ncc-test2 --skip-first-line --debug
-
-

Contents of ip-ranges-ncc-test2.json

-
{
-    "createDate": "2015-11-17-10-07-22",
-    "prefixes": [
-        {
-            "ip_prefix": "3.3.3.0/24",
-            "name": " SF Office"
-        }
-    ]
-}
-
-

Manually create and update ip-ranges

- -

In case CIDRs were not managed in a CSV file, the tools offers an interactive -mode that may be leveraged to manually create a JSON ip-ranges file. The -following snippet illustrates how to use the tool to interactively create new -ip-ranges JSON files:

-
$ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name
-
-Add a new IP prefix to the ip ranges (y/n)? 
-y
-Enter the new IP prefix:
-5.5.5.0/24
-You entered "5.5.5.0/24". Is that correct (y/n)? 
-y
-Enter the 'name' value:
-San Francisco
-You entered "San Francisco". Is that correct (y/n)? 
-y
-Add a new IP prefix to the ip ranges (y/n)? 
-y
-Enter the new IP prefix:
-6.6.6.6/32
-You entered "6.6.6.6/32". Is that correct (y/n)? 
-y
-Enter the 'name' value:
-San Francisco
-You entered "San Francisco". Is that correct (y/n)? 
-y
-Add a new IP prefix to the ip ranges (y/n)? 
-n
-
-

Contents of ip-ranges-ncc-offices.json:

-
{
-    "createDate": "2015-11-16-22-44-38",
-    "prefixes": [
-        {
-            "ip_prefix": "5.5.5.0/24",
-            "name": "San Francisco"
-        },
-        {
-            "ip_prefix": "6.6.6.6/32",
-            "name": "San Francisco"
-        }
-    ]
-}
-
-

The tool can also automatically add new CIDRs to existing ip-ranges files:

-
$ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name --debug
-
-Loading existing IP ranges from ip-ranges-ncc-offices.json
-Add a new IP prefix to the ip ranges (y/n)? 
-y
-Enter the new IP prefix:
-7.7.7.7/32
-You entered "7.7.7.7/32". Is that correct (y/n)? 
-y
-Enter the 'name' value:
-Seattle
-You entered "Seattle". Is that correct (y/n)? 
-y
-Add a new IP prefix to the ip ranges (y/n)? 
-n
-File 'ip-ranges-ncc-offices.json' already exists. Do you want to overwrite it (y/n)? 
-y
-
-$ cat ip-ranges-ncc-offices.json 
-{
-    "createDate": "2015-11-16-22-44-38",
-    "prefixes": [
-        {
-            "ip_prefix": "5.5.5.0/24",
-            "name": "San Francisco"
-        },
-        {
-            "ip_prefix": "6.6.6.6/32",
-            "name": "San Francisco"
-        },
-        {
-            "ip_prefix": "7.7.7.7/32",
-            "name": "Seattle"
-        }
-    ]
-}
-
-

Conclusion

- -

This addition to Scout2 provides AWS account administrators and auditors with -an improved insight into their environment. Usage of this feature should result -in further hardened security groups because detection of unknown whitelisted -CIDRs and understanding of existing rules is significantly easier.

- -

I am currently working on a major rework of Scout2's reporting engine, -which will further improve reporting and allow creation of new alerts when an -unknown CIDR is whitelisted.

- - -
- -
- - - -
- - - - -
- - - -

- - Redshift support added in Scout2 - -

- -
- Published on August 6, 2015 -
- - - - -
- [Originally published on NCC Group's blog] -
- - - -
- -

Today, I am excited to announce that support for Redshift was added in -Scout2. By default, Scout2 will fetch information about your Redshift clusters, -cluster parameter groups, and cluster security groups if you still use -EC2-Classic. At this stage, Scout2 comes with six Redshift security rules that -are enabled by default:

- -
    -
  • Clusters - -
      -
    • Check whether version upgrade is enabled
    • -
    • Check whether the cluster is publicly accessible
    • -
    • Check whether database encryption is enabled
    • -
  • -
  • Cluster parameter groups - -
      -
    • Check whether SSL/TLS is required to access the database
    • -
    • Check whether user activity logging is enabled
    • -
  • -
  • Cluster security groups (EC2-classic) - -
      -
    • Check whether the security group allows access to all IP addresses (0.0.0.0/0)
    • -
  • -
- -

Scout2 was first released over a year and a half ago, and proved to be extremely -helpful when performing AWS configuration reviews. While -Scout2's initial release only supported three services (IAM, EC2, and S3) and -included thirteen security checks, the tool rapidly grew to add support for RDS -and CloudTrail. Furthermore, the tool now offers over fifty tests throughout -these five supported services. I hope that support for Redshift will bring -value to users of Scout2, and welcome feature requests, bug reports, and -recommendations on Github at -https://github.com/iSECPartners/Scout2/issues.

- - -
- -
- - - -
- - - - -
- - - -

- - Introducing opinel: Scout2's favorite tool - -

- -
- Published on August 3, 2015 -
- - -
- [Originally published on iSEC Partners's research blog] -
- - - - - -
- -

With boto3 being stable and generally available1, I took the opportunity -to migrate Scout2 and AWS-recipes to boto3. As part of that migration -effort, I decided to publish the formerly-known-as AWSUtils repository -- used -by Scout2 and AWS-recipes -- as a python package required by these tools, -rather than requiring users to work with Git submodules. I've also added more -flexibility when working with MFA-protected API calls and improved versioning -across the project.

- -

opinel

- -

To avoid name conflicts, I decided to rename the shared AWSUtils code to a -less misleading name: opinel. The opinel package is published on PyPI, and thus can -be installed using pip and easy_install. The corresponding source code is still -open-sourced on Github at https://github.com/iSECPartners/opinel. -As a result, Scout2 and AWS-recipes have been modified to list opinel as a -requirement, which significantly simplifies installation and management of this -shared code.

- -

Support for Python 2.7 and 3.x

- -

Because boto3 supports both Python2 and Python3, I decided to make sure that -the code built on top of that package has similar properties. As a result, -the latest versions of Scout2 and AWS-recipes support Python 2.7 and 3.x. -Note that opinel will NOT work with Python 2.6.

- -

Modification of the MFA workflow

- -

As requested by a user of AWS-recipes2, I modified the workflow when -using MFA-protected API access to no longer store the long-lived credentials -in a separate file. As a result, the .aws/credentials.no-mfa file is no -longer supported and all credentials are stored in the standard AWS credentials -file under .aws/credentials. Usage of the existing tools remains unchanged, -but the long-lived credentials are now accessible via a new profile name: -profile_name-nomfa. This allows users to work with both STS and long-lived -credentials if need be.

- -

If you already had configured your environment to work with MFA-protected API -access, you will need to copy your long-lived credentials back to the -.aws/credentials file. This can be done with a simple command such as the -following:

-
cat ~/.aws/credentials.no-mfa | sed -e 's/]$/-nomfa]/g' >> ~/.aws/credentials
-
-

Support to use assumed-role credentials

- -

With this new workflow implemented, I created a new recipe that allows -configuration of role-credentials in the .aws/credentials file. When the following -command is run, it uses the credentials associated with the isecpartners -profile to request role credentials for the IAM-Scout2 role. The role -credentials are then written in the .aws/credentials file in a new profile -named isecpartners-Scout2, which is the profile name appended by the role -session name.

-
$ ./aws_recipes_assume_role.py --profile isecpartners --role-arn arn:aws:iam::AWS_ACCOUNT_ID:role/IAM-Scout2 --role-session-name Scout2
-
-

Users can then use their favorite tools that support profiles. For example, -Scout2 could be run with the following command line:

-
$ ./Scout2.py --profile isecpartners-Scout2
-
-

Note that this recipe supports MFA if the assumed role requires it:

- -
    -
  • If you never configured your environment to work with MFA, you can provide your MFA serial number (ARN) and current token code as arguments.
  • -
  • If you already configured your environment to work with MFA and stored your MFA serial in the .aws/credentials file, you just need to pass your token code as an additional argument.
  • -
  • Finally, if you already initiated an STS session, you do not need to provide a new token code and can run the command as above.
  • -
- -

Conclusion

- -

With the release of opinel, I hope to simplify distribution and management of -the code shared between Scout2 and AWS-recipes. Additionally, I -significantly modified the workflow and credentials storage when working with -MFA-protected API calls, which allows users to use both their long-lived and STS -credentials.

- - -
- -
- - - -
- - - - -
- - - -

- - IAM user management strategy (part 2) - -

- -
- Published on June 9, 2015 -
- - -
- [Originally published on iSEC Partners's research blog] -
- - - - - -
- -

The previous IAM user management strategy post discussed how -usage of IAM groups enables AWS administrators to consistently grant privileges -and enforce a number of security rules (such as MFA-protected API access). This -blog post will build on this idea by introducing category groups and -documenting new tools to improve IAM user management.

- -

Categorize your IAM users

- -

For a variety of reasons, applying a single set of security rules to all IAM -users is not always practical. For example, because many applications running -in AWS predate IAM roles, numerous environments still rely on the existence of -headless IAM users. Additionally, third parties may be granted access to an AWS -account for a number of reasons but may not be able to comply with the same set -of security rules that employees follow. For this reason, NCC recommends using -category groups to sort IAM users and reliably enforce appropriate security -measures. For example, one group for all human users and a second for all headless users may be -created: MFA-protected API access and password management are not relevant for -headless users. Furthermore, human users may be categorized into several groups -such as employees and contractors: API access can be restricted to the -corporate IP range for employees but might not be achievable for contractors.

- -

Note 1: The set of category groups should define all types of IAM users that -may exist in your AWS account and each IAM user should belong to one -- and -only one -- category group (they may belong to other groups though).

- -

Note 2: The common group and category groups should be used to enable enforcing -security in one's AWS environment. Policies attached to these groups should be -carefully reviewed and grant the minimum set of privileges necessary for this -type of IAM user (e.g. credential management for humans).

- -

Example of category groups

- -

The rest of this article describes a number of tools developed and used by -NCC to help implement this IAM user management strategy. These tools can be found -in the AWS-Recipes repository. We will -use our test AWS environment as an example, in which we use three category groups in -addition to the AllUsers common group:

- -
    -
  1. AllHumans, the group all employees must belong to.
  2. -
  3. AllHeadlessUsers, the group all headless IAM users must belong to.
  4. -
  5. AllMisconfiguredUsers, a placeholder for sample misconfigured users.
  6. -
- -

We also have an IAM user naming convention that requires usernames to match the -following schema:

- -
    -
  1. Employees: firstname initial appended with lastname
  2. -
  3. Headless user: name of the service prefixed with HeadlessUser-
  4. -
  5. Misconfigured: description of the misconfiguration prefixed with MisconfiguredUser-
  6. -
- -

Based on these rules, we created a configuration file stored under -.aws/recipes/isecpartners.json, with isecpartners matching the profile's -name. If you do not use profiles, the configuration will be under -.aws/recipes/default.json.

-
{
-    "common_groups": [ "AllUsers" ],
-    "category_groups": [
-        "AllHumanUsers",
-        "AllHeadlessUsers",
-        "AllMisconfiguredUsers"
-    ],
-    "category_regex": [
-        "",
-        "^Headless-(.*)",
-        "^MisconfiguredUser-(.*)"
-    ],
-    "profile_name": [ "isecpartners" ]
-}
-
-

This configuration file declares the name of the common IAM group and two lists -related to the categorization of IAM users:

- -
    -
  1. A list of category groups.
  2. -
  3. A list of regular expressions matching our naming convention.
  4. -
- -

Note 1: If you do not have a naming convention in place to distinguish the -type of user, remove the category_regex attribute from your configuration -file.

- -

Note 2: If a regular expression is only applicable to a subset of category -groups, you must ensure that both lists have the same length and use an empty -string for groups that cannot be automatically associated (see the -AllHumanUsers group in our example).

- -

Note 3: Use of a configuration file is not necessary as all values may be -passed as command line arguments. If a configuration file exists and a value is -passed as an argument, the value passed via the command line will be used.

- -

Create your default groups with aws_iam_create_default_groups.py

- -

The purpose of this tool is to create IAM groups whose name matches the common -and category groups specified in the above configuration file. Running the -following command results in four new groups being created if they did not -already exist.

-
./aws_iam_create_default_groups.py --profile isecpartners
-
-

(Automatically) sort IAM users with aws_iam_sort_users.py.

- -

This tool iterates through all IAM users and attempts to automatically detect -the IAM groups each user should belong to. For convenience, we recommend adding -the following to your AWS recipes configuration files:

-
"aws_sort_users.py": {
-    "create_groups": false,
-},
-"force_common_group": true
-
-

This specifies default values for additional arguments to be set when running -aws_iam_sort_users.py. Specifically, with these values, running this tool -will automatically add all IAM users to the common group AllUsers and will -not attempt to create the default groups (not necessary as we already did -this). Additionally, this tool checks that each IAM user belongs to one of the -category groups. If this is not the case and the username matches a regular -expression, the user is automatically added to the matching category group. Otherwise, a -multi-choice prompt appears to allow manual selection of the appropriate -category group.

- -

Additional advantages of configuration files

- -

Besides helping with simplification of these tools' usage, this new AWS-recipe -configuration file can be used across tools, allowing for more consistent -rule enforcement. For example, the -aws_iam_create_user.py. -tool uses this configuration file and applies the same business logic to add -users to the common group and appropriate category group at user creation time. In -our test environment, for example, running the following command automatically -added the new user to the MisconfiguredUser group:

-
$ ./aws_iam_create_user.py --profile isecpartners --users MisconfiguredUser-BlogPostExample
-Creating user MisconfiguredUser-BlogPostExample...
-Save unencrypted value (y/n)? y
-User 'MisconfiguredUser-BlogPostExample' does not belong to the mandatory common group 'AllUsers'. Do you want to remediate this now (y/n)? y
-User 'MisconfiguredUser-BlogPostExample' does not belong to any of the category group (AllHumanUsers, AllHeadlessUsers, AllMisconfiguredUsers). Automatically adding...
-Enabling MFA for user MisconfiguredUser-BlogPostExample...
-
-

Conclusion

- -

While efficient and reliable management of IAM users can be challenging, using -the right strategy and tools significantly simplifies this process. Creation -and use of a naming convention for IAM users enables -automated user management and enforcement of security rules.

- - -
- -
- - - - - - - - - - - - - -
-
-
- - - - - - - - - - - - -
-
-
- - - - - - - - diff --git a/_site/slides/2016-10-21-IAM-Roles-Beyond-EC2-Instances-Security-In-Depth-For-AWS.pdf b/_site/slides/2016-10-21-IAM-Roles-Beyond-EC2-Instances-Security-In-Depth-For-AWS.pdf deleted file mode 100644 index 0e00cc1..0000000 Binary files a/_site/slides/2016-10-21-IAM-Roles-Beyond-EC2-Instances-Security-In-Depth-For-AWS.pdf and /dev/null differ diff --git a/_site/slides/index.html b/_site/slides/index.html deleted file mode 100644 index 77eb8c2..0000000 --- a/_site/slides/index.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - l01cd3v.github.io - - - -
-
-
-

l01cd3v.github.io

-

by Loïc

- -
-
- -
-
- - - - - - - - - - - - -
-
-
- - - - - - - - diff --git a/_site/slides/us-16-Simon-Access-Keys-Will-Kill-You-Before-You-Kill-The-Password.pdf b/_site/slides/us-16-Simon-Access-Keys-Will-Kill-You-Before-You-Kill-The-Password.pdf deleted file mode 100644 index 350371a..0000000 Binary files a/_site/slides/us-16-Simon-Access-Keys-Will-Kill-You-Before-You-Kill-The-Password.pdf and /dev/null differ diff --git a/_site/stylesheets/print.css b/_site/stylesheets/print.css deleted file mode 100644 index 4b19b67..0000000 --- a/_site/stylesheets/print.css +++ /dev/null @@ -1,228 +0,0 @@ -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - padding: 0; - margin: 0; - font: inherit; - font-size: 100%; - vertical-align: baseline; - border: 0; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-spacing: 0; - border-collapse: collapse; -} -body { - font-family: 'Helvetica Neue', Helvetica, Arial, serif; - font-size: 13px; - line-height: 1.5; - color: #000; -} - -a { - font-weight: bold; - color: #d5000d; -} - -header { - padding-top: 35px; - padding-bottom: 10px; -} - -header h1 { - font-size: 48px; - font-weight: bold; - line-height: 1.2; - color: #303030; - letter-spacing: -1px; -} - -header h2 { - font-size: 24px; - font-weight: normal; - line-height: 1.3; - color: #aaa; - letter-spacing: -1px; -} -#downloads { - display: none; -} -#main_content { - padding-top: 20px; -} - -code, pre { - margin-bottom: 30px; - font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal; - font-size: 12px; - color: #222; -} - -code { - padding: 0 3px; -} - -pre { - padding: 20px; - overflow: auto; - border: solid 1px #ddd; -} -pre code { - padding: 0; -} - -ul, ol, dl { - margin-bottom: 20px; -} - - -/* COMMON STYLES */ - -table { - width: 100%; - border: 1px solid #ebebeb; -} - -th { - font-weight: 500; -} - -td { - font-weight: 300; - text-align: center; - border: 1px solid #ebebeb; -} - -form { - padding: 20px; - background: #f2f2f2; - -} - - -/* GENERAL ELEMENT TYPE STYLES */ - -h1 { - font-size: 2.8em; -} - -h2 { - margin-bottom: 8px; - font-size: 22px; - font-weight: bold; - color: #303030; -} - -h3 { - margin-bottom: 8px; - font-size: 18px; - font-weight: bold; - color: #d5000d; -} - -h4 { - font-size: 16px; - font-weight: bold; - color: #303030; -} - -h5 { - font-size: 1em; - color: #303030; -} - -h6 { - font-size: .8em; - color: #303030; -} - -p { - margin-bottom: 20px; - font-weight: 300; -} - -a { - text-decoration: none; -} - -p a { - font-weight: 400; -} - -blockquote { - padding: 0 0 0 30px; - margin-bottom: 20px; - font-size: 1.6em; - border-left: 10px solid #e9e9e9; -} - -ul li { - list-style-position: inside; - list-style: disc; - padding-left: 20px; -} - -ol li { - list-style-position: inside; - list-style: decimal; - padding-left: 3px; -} - -dl dd { - font-style: italic; - font-weight: 100; -} - -footer { - padding-top: 20px; - padding-bottom: 30px; - margin-top: 40px; - font-size: 13px; - color: #aaa; -} - -footer a { - color: #666; -} - -/* MISC */ -.clearfix:after { - display: block; - height: 0; - clear: both; - visibility: hidden; - content: '.'; -} - -.clearfix {display: inline-block;} -* html .clearfix {height: 1%;} -.clearfix {display: block;} diff --git a/_site/stylesheets/pygment_trac.css b/_site/stylesheets/pygment_trac.css deleted file mode 100644 index c6a6452..0000000 --- a/_site/stylesheets/pygment_trac.css +++ /dev/null @@ -1,69 +0,0 @@ -.highlight { background: #ffffff; } -.highlight .c { color: #999988; font-style: italic } /* Comment */ -.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ -.highlight .k { font-weight: bold } /* Keyword */ -.highlight .o { font-weight: bold } /* Operator */ -.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ -.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ -.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ -.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ -.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ -.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */ -.highlight .ge { font-style: italic } /* Generic.Emph */ -.highlight .gr { color: #aa0000 } /* Generic.Error */ -.highlight .gh { color: #999999 } /* Generic.Heading */ -.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ -.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */ -.highlight .go { color: #888888 } /* Generic.Output */ -.highlight .gp { color: #555555 } /* Generic.Prompt */ -.highlight .gs { font-weight: bold } /* Generic.Strong */ -.highlight .gu { color: #800080; font-weight: bold; } /* Generic.Subheading */ -.highlight .gt { color: #aa0000 } /* Generic.Traceback */ -.highlight .kc { font-weight: bold } /* Keyword.Constant */ -.highlight .kd { font-weight: bold } /* Keyword.Declaration */ -.highlight .kn { font-weight: bold } /* Keyword.Namespace */ -.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ -.highlight .kr { font-weight: bold } /* Keyword.Reserved */ -.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ -.highlight .m { color: #009999 } /* Literal.Number */ -.highlight .s { color: #d14 } /* Literal.String */ -.highlight .na { color: #008080 } /* Name.Attribute */ -.highlight .nb { color: #0086B3 } /* Name.Builtin */ -.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ -.highlight .no { color: #008080 } /* Name.Constant */ -.highlight .ni { color: #800080 } /* Name.Entity */ -.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ -.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ -.highlight .nn { color: #555555 } /* Name.Namespace */ -.highlight .nt { color: #000080 } /* Name.Tag */ -.highlight .nv { color: #008080 } /* Name.Variable */ -.highlight .ow { font-weight: bold } /* Operator.Word */ -.highlight .w { color: #bbbbbb } /* Text.Whitespace */ -.highlight .mf { color: #009999 } /* Literal.Number.Float */ -.highlight .mh { color: #009999 } /* Literal.Number.Hex */ -.highlight .mi { color: #009999 } /* Literal.Number.Integer */ -.highlight .mo { color: #009999 } /* Literal.Number.Oct */ -.highlight .sb { color: #d14 } /* Literal.String.Backtick */ -.highlight .sc { color: #d14 } /* Literal.String.Char */ -.highlight .sd { color: #d14 } /* Literal.String.Doc */ -.highlight .s2 { color: #d14 } /* Literal.String.Double */ -.highlight .se { color: #d14 } /* Literal.String.Escape */ -.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ -.highlight .si { color: #d14 } /* Literal.String.Interpol */ -.highlight .sx { color: #d14 } /* Literal.String.Other */ -.highlight .sr { color: #009926 } /* Literal.String.Regex */ -.highlight .s1 { color: #d14 } /* Literal.String.Single */ -.highlight .ss { color: #990073 } /* Literal.String.Symbol */ -.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #008080 } /* Name.Variable.Class */ -.highlight .vg { color: #008080 } /* Name.Variable.Global */ -.highlight .vi { color: #008080 } /* Name.Variable.Instance */ -.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ - -.type-csharp .highlight .k { color: #0000FF } -.type-csharp .highlight .kt { color: #0000FF } -.type-csharp .highlight .nf { color: #000000; font-weight: normal } -.type-csharp .highlight .nc { color: #2B91AF } -.type-csharp .highlight .nn { color: #000000 } -.type-csharp .highlight .s { color: #A31515 } -.type-csharp .highlight .sc { color: #A31515 } diff --git a/_site/stylesheets/stylesheet.css b/_site/stylesheets/stylesheet.css deleted file mode 100644 index 63557b9..0000000 --- a/_site/stylesheets/stylesheet.css +++ /dev/null @@ -1,421 +0,0 @@ -/* http://meyerweb.com/eric/tools/css/reset/ - v2.0 | 20110126 - License: none (public domain) -*/ -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - padding: 0; - margin: 0; - font: inherit; - font-size: 100%; - vertical-align: baseline; - border: 0; -} -/* HTML5 display-role reset for older browsers */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; -} -body { - line-height: 1; -} -ol, ul { - list-style: none; -} -blockquote, q { - quotes: none; -} -blockquote:before, blockquote:after, -q:before, q:after { - content: ''; - content: none; -} -table { - border-spacing: 0; - border-collapse: collapse; -} - -/* LAYOUT STYLES */ -body { - font-family: 'Helvetica Neue', Helvetica, Arial, serif; - font-size: 1em; - line-height: 1.5; - color: #6d6d6d; - text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8); - background: #e7e7e7 url(../images/body-bg.png) 0 0 repeat; -} - -a { - color: #1060D0; -} -a:hover { - color: #104090; -} - -header { - padding-top: 35px; - padding-bottom: 25px; -} - -header h1 { - font-family: 'Chivo', 'Helvetica Neue', Helvetica, Arial, serif; - font-size: 48px; font-weight: 900; - line-height: 1.2; - color: #303030; - letter-spacing: -1px; - text-align: left; -} - -header h2 { - font-size: 24px; - font-weight: normal; - line-height: 1.3; - color: #aaa; - letter-spacing: -1px; -} - -header hr { - margin-bottom: 10px; -} - -nav { - text-align: right; - margin: -15px 20px 0px 0px; -} - -.subtitle { - margin: 0px 0px 0px 20px; -} - -#container { - min-height: 595px; - background: transparent url(../images/highlight-bg.jpg) 50% 0 no-repeat; -} - -.inner { - width: 800px; - margin: 0 auto; -} - -#container .inner img { - max-width: 100%; -} - - -a.button { - display: block; - float: left; - width: 179px; - padding: 12px 8px 12px 8px; - margin-right: 14px; - font-size: 15px; - font-weight: bold; - line-height: 25px; - color: #303030; - background: #fdfdfd; /* Old browsers */ - background: -moz-linear-gradient(top, #fdfdfd 0%, #f2f2f2 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fdfdfd), color-stop(100%,#f2f2f2)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #fdfdfd 0%,#f2f2f2 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #fdfdfd 0%,#f2f2f2 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #fdfdfd 0%,#f2f2f2 100%); /* IE10+ */ - background: linear-gradient(top, #fdfdfd 0%,#f2f2f2 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fdfdfd', endColorstr='#f2f2f2',GradientType=0 ); /* IE6-9 */ - border-top: solid 1px #cbcbcb; - border-right: solid 1px #b7b7b7; - border-bottom: solid 1px #b3b3b3; - border-left: solid 1px #b7b7b7; - border-radius: 30px; - -webkit-box-shadow: 10px 10px 5px #888; - -moz-box-shadow: 10px 10px 5px #888; - box-shadow: 0px 1px 5px #e8e8e8; - -moz-border-radius: 30px; - -webkit-border-radius: 30px; -} -a.button:hover { - background: #fafafa; /* Old browsers */ - background: -moz-linear-gradient(top, #fdfdfd 0%, #f6f6f6 100%); /* FF3.6+ */ - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#fdfdfd), color-stop(100%,#f6f6f6)); /* Chrome,Safari4+ */ - background: -webkit-linear-gradient(top, #fdfdfd 0%,#f6f6f6 100%); /* Chrome10+,Safari5.1+ */ - background: -o-linear-gradient(top, #fdfdfd 0%,#f6f6f6 100%); /* Opera 11.10+ */ - background: -ms-linear-gradient(top, #fdfdfd 0%,#f6f6f6 100%); /* IE10+ */ - background: linear-gradient(top, #fdfdfd 0%,#f6f6f6, 100%); /* W3C */ - filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fdfdfd', endColorstr='#f6f6f6',GradientType=0 ); /* IE6-9 */ - border-top: solid 1px #b7b7b7; - border-right: solid 1px #b3b3b3; - border-bottom: solid 1px #b3b3b3; - border-left: solid 1px #b3b3b3; -} - -a.button span { - display: block; - height: 23px; - padding-left: 50px; -} - -#download-zip span { - background: transparent url(../images/zip-icon.png) 12px 50% no-repeat; -} -#download-tar-gz span { - background: transparent url(../images/tar-gz-icon.png) 12px 50% no-repeat; -} -#view-on-github span { - background: transparent url(../images/octocat-icon.png) 12px 50% no-repeat; -} -#view-on-github { - margin-right: 0; -} - -code, pre { - margin-bottom: 30px; - font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal; - font-size: 14px; - color: #222; -} - -code { - padding: 0 3px; - background-color: #f2f2f2; - border: solid 1px #ddd; -} - -pre { - padding: 20px; - overflow: auto; - color: #f2f2f2; - text-shadow: none; - background: #303030; -} -pre code { - padding: 0; - color: #f2f2f2; - background-color: #303030; - border: none; -} - -ul, ol, dl { - margin-bottom: 20px; - margin-left: 18px; -} - - -/* COMMON STYLES */ - -hr { - height: 1px; - padding-bottom: 1em; - margin-top: 1em; - margin-bottom: 30px; - line-height: 1px; - background: transparent url('../images/hr.png') 50% 0 no-repeat; - border: none; -} - -strong { - font-weight: bold; -} - -em { - font-style: italic; -} - -table { - width: 100%; - border: 1px solid #ebebeb; -} - -th { - font-weight: 500; -} - -td { - font-weight: 300; - text-align: center; - border: 1px solid #ebebeb; -} - -form { - padding: 20px; - background: #f2f2f2; - -} - - -/* GENERAL ELEMENT TYPE STYLES */ - -h1 { - font-size: 32px; - text-align: center; - margin: -30px 0px 0px 0px; -} - -h2 { - margin-top: 5px; - margin-bottom: 15px; - font-size: 22px; - font-weight: bold; - color: #303030; - padding-left: 10px; -} - -h3 { - margin-bottom: 8px; - font-size: 18px; - font-weight: bold; - color: #1060D0; -} - -h4 { - font-size: 16px; - font-weight: bold; - color: #303030; -} - -h5 { - font-size: 1em; - color: #303030; -} - -h6 { - font-size: .9em; - text-align: right; - margin-top: -10px; - margin-bottom: 15px; -} - -p { - margin-bottom: 20px; - font-weight: 300; -} - -a { - text-decoration: none; -} - -p a { - font-weight: 400; -} - -blockquote { - padding: 0 0 0 30px; - margin-bottom: 20px; - font-size: 1.6em; - border-left: 10px solid #e9e9e9; -} - -ul li { - list-style-position: inside; - list-style: disc; - padding-left: 5px; -} - -ol li { - list-style-position: inside; - list-style: decimal; - padding-left: 3px; -} - -dl dt { - color: #303030; -} - -footer { - padding-top: 10px; - padding-bottom: 20px; - margin-top: 0px; - font-size: 13px; - color: #aaa; - text-align: center; -} - -footer a { - color: #666; -} -footer a:hover { - color: #444; -} - -footer hr{ - margin-bottom: 0px; -} - -/* MISC */ -.clearfix:after { - display: block; - height: 0; - clear: both; - visibility: hidden; - content: '.'; -} - -.clearfix {display: inline-block;} -* html .clearfix {height: 1%;} -.clearfix {display: block;} - -.date { - text-align: right; - margin-bottom: 10px; -} - -.post { - text-align: justify; - text-justify: inter-word; -} - -.post-list p { - margin: 0px 0px 0px 0px; -} - -.post-list-title { - max-width: 100%; - overflow:hidden; - float: left; -} - -.post-list-date { - overflow:hidden; - text-align: right; -} - -/* #Media Queries -================================================== */ - -/* Smaller than standard 960 (devices and browsers) */ -@media only screen and (max-width: 959px) { } - -/* Tablet Portrait size to standard 960 (devices and browsers) */ -@media only screen and (min-width: 768px) and (max-width: 959px) { } - -/* All Mobile Sizes (devices and browser) */ -@media only screen and (max-width: 767px) { - header { - padding-top: 10px; - padding-bottom: 10px; - } - #downloads { - margin-bottom: 25px; - } - #download-zip, #download-tar-gz { - display: none; - } - .inner { - width: 94%; - margin: 0 auto; - } -} - -/* Mobile Landscape Size to Tablet Portrait (devices and browsers) */ -@media only screen and (min-width: 480px) and (max-width: 767px) { } - -/* Mobile Portrait Size to Mobile Landscape Size (devices and browsers) */ -@media only screen and (max-width: 479px) { } diff --git a/_site/stylesheets/svg-icons.css b/_site/stylesheets/svg-icons.css deleted file mode 100644 index 7e4387b..0000000 --- a/_site/stylesheets/svg-icons.css +++ /dev/null @@ -1,27 +0,0 @@ -.svg-icon { - width: 40px; - height: 40px; - display: inline-block; -} - -.dribbble { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPjxwYXRoIGQ9Ik0wLjIyNDU4MDY4OCwzMCBDMC4yMjQ1ODA2ODgsMTMuNDMxNDU2NyAxMy40NTQ5NDEsMCAyOS43NzU0MTkzLDAgQzQ2LjA5NTg5NzYsMCA1OS4zMjYyNTc5LDEzLjQzMTQ1NjcgNTkuMzI2MjU3OSwzMCBDNTkuMzI2MjU3OSw0Ni41Njg1NDMzIDQ2LjA5NTg5NzYsNjAgMjkuNzc1NDE5Myw2MCBDMTMuNDU0OTQxLDYwIDAuMjI0NTgwNjg4LDQ2LjU2ODU0MzMgMC4yMjQ1ODA2ODgsMzAgWiBNMC4yMjQ1ODA2ODgsMzAiIGZpbGw9IiNFQTRDODkiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD48cGF0aCBkPSJNNDYuODIwMTQwMiwyOS44MzA2MzA3IEM0Ni44MjAxNDAyLDMwLjk5MTA2NjIgNDYuNzAzOTM4MSwzMi4xNDk4OTIgNDYuNDc1Njk1OCwzMy4yNzk0NzczIEM0Ni4yNTMxMzQ4LDM0LjM4MjIzNjMgNDUuOTIzNDg4MSwzNS40NjQ4NzU3IDQ1LjQ5MTY0NDQsMzYuNDk5ODMxNCBDNDUuMDY5NzA5OSwzNy41MTY0NzgzIDQ0LjU0NjM3MSwzOC40OTYxNzIgNDMuOTM0ODQwMiwzOS40MDk4MDYyIEM0My4zMzI0MjU4LDQwLjMxNzY3MjcgNDIuNjM4NDUwMiw0MS4xNjk0MDUzIDQxLjg3NjEwMSw0MS45NDU4MjM0IEM0MS4xMTIxMDAzLDQyLjcxODg4ODEgNDAuMjcxNDY4Myw0My40MjI0NzI5IDM5LjM3ODkxMiw0NC4wMzc1MzEyIEMzOC40NzczMDUzLDQ0LjY1NDkzNjcgMzcuNTEyMjc5Niw0NS4xODYyMzAxIDM2LjUxMTcxMjgsNDUuNjE3MTkzNCBDMzUuNDkyMjUyNSw0Ni4wNTM5MjQ0IDM0LjQyNDg5NzUsNDYuMzg4NTgxNSAzMy4zNDExNTk0LDQ2LjYxNDQ1ODMgQzMyLjIyODYxODUsNDYuODQ3MTA4OCAzMS4wODQ2MzIzLDQ2Ljk2NTg4MTkgMjkuOTQxNTcwOSw0Ni45NjU4ODE5IEMyOC43OTc2NTA4LDQ2Ljk2NTg4MTkgMjcuNjUzNzMwNyw0Ni44NDcxMDg4IDI2LjU0Mjc3NTIsNDYuNjE0NDU4MyBDMjUuNDU3Mzg1NSw0Ni4zODg1ODE1IDI0LjM5MDA5NjcsNDYuMDUzOTI0NCAyMy4zNzE0MjkxLDQ1LjYxNzE5MzQgQzIyLjM3MDkyODQsNDUuMTg2MjMwMSAyMS40MDQ5Nzc4LDQ0LjY1NDkzNjcgMjAuNTAzMzcxMSw0NC4wMzc1MzEyIEMxOS42MTA4MTQ4LDQzLjQyMjU0IDE4Ljc3MDE4MjgsNDIuNzE4ODg4MSAxOC4wMDc4MzM2LDQxLjk0NTgyMzQgQzE3LjI0NDY5MTcsNDEuMTY5NDA1MyAxNi41NTA3MTYxLDQwLjMxNzY3MjcgMTUuOTQ3NDQyOSwzOS40MDk4MDYyIEMxNS4zMzkyMTUxLDM4LjQ5NjE3MiAxNC44MTUwMTc1LDM3LjUxNjQxMTIgMTQuMzkxNDMxNCwzNi40OTk4MzE0IEMxMy45NTk1MjE2LDM1LjQ2NDg3NTcgMTMuNjI5MDgyMywzNC4zODIxNjkyIDEzLjQwNTcyODUsMzMuMjc5NDc3MyBDMTMuMTc5OTMwNCwzMi4xNDk4OTIgMTMuMDY0NTIxMSwzMC45OTEwNjYyIDEzLjA2NDUyMTEsMjkuODMwNjMwNyBDMTMuMDY0NTIxMSwyOC42NjkzMjM1IDEzLjE3OTg2NDQsMjcuNTA4MDE2MiAxMy40MDU3OTQ1LDI2LjM4MDkxMjMgQzEzLjYyOTE0ODMsMjUuMjc4MTUzMyAxMy45NTk1ODc3LDI0LjE5MzgzNzMgMTQuMzkxNDk3NSwyMy4xNjA1NTgyIEMxNC44MTUwODM1LDIyLjE0MzE3MzYgMTUuMzM5MjgxMiwyMS4xNjI2MDggMTUuOTQ3NTA5LDIwLjI0ODkwNjggQzE2LjU1MDc4MjIsMTkuMzQwMzAyNSAxNy4yNDQ3NTc3LDE4LjQ5MDE3OTUgMTguMDA3ODk5NiwxNy43MTI4ODk2IEMxOC43NzAyNDg4LDE2LjkzOTc1NzggMTkuNjEwODgwOCwxNi4yMzc4NDk2IDIwLjUwMzQzNzEsMTUuNjIzNzMwMyBDMjEuNDA1MDQzOCwxNS4wMDM3MDkyIDIyLjM3MDk5NDQsMTQuNDcyNDgyOCAyMy4zNzE0OTUxLDE0LjA0MDcxNDcgQzI0LjM5MDE2MjcsMTMuNjAzMDQ0OSAyNS40NTczODU1LDEzLjI2NzU4MjkgMjYuNTQyODQxMywxMy4wNDMzODI4IEMyNy42NTM3OTY3LDEyLjgxMjQ3NiAyOC43OTc3MTY5LDEyLjY5NTMxMjUgMjkuOTQxNjM3LDEyLjY5NTMxMjUgQzMxLjA4NDY5ODMsMTIuNjk1MzEyNSAzMi4yMjg2MTg1LDEyLjgxMjQ3NiAzMy4zNDEyMjU1LDEzLjA0MzM4MjggQzM0LjQyNDk2MzYsMTMuMjY3NjUgMzUuNDkyMjUyNSwxMy42MDMxMTIgMzYuNTExNzc4OSwxNC4wNDA3MTQ3IEMzNy41MTIyNzk2LDE0LjQ3MjQxNTggMzguNDc3MzcxNCwxNS4wMDM3MDkyIDM5LjM3ODk3ODEsMTUuNjIzNzMwMyBDNDAuMjcxNTM0NCwxNi4yMzc4NDk2IDQxLjExMjIzMjQsMTYuOTM5NzU3OCA0MS44NzYxNjcxLDE3LjcxMjg4OTYgQzQyLjYzODUxNjMsMTguNDkwMTc5NSA0My4zMzI0OTE4LDE5LjM0MDMwMjUgNDMuOTM0OTA2MiwyMC4yNDg5MDY4IEM0NC41NDYzNzEsMjEuMTYyNjA4IDQ1LjA2OTcwOTksMjIuMTQzMjQwNyA0NS40OTE2NDQ0LDIzLjE2MDU1ODIgQzQ1LjkyMzQ4ODEsMjQuMTkzODM3MyA0Ni4yNTMxMzQ4LDI1LjI3ODE1MzMgNDYuNDc1Njk1OCwyNi4zODA5MTIzIEM0Ni43MDM5MzgxLDI3LjUwODAxNjIgNDYuODIwMTQwMiwyOC42NjkzMjM1IDQ2LjgyMDE0MDIsMjkuODMwNjMwNyBaIE0yMy43OTE3MTIsMTYuNTk1MTA3OSBDMTkuNzcyMjY5LDE4LjUyMjAzNTYgMTYuNzcyMzUyMywyMi4yODIwNjY0IDE1LjgzNjkyMjEsMjYuODEzNTUyMyBDMTYuMjE2OTA3NiwyNi44MTY5MDU2IDIyLjIyMzM0NzEsMjYuODkzODk2OCAyOS4xNDM3NDY4LDI1LjAyNzE5NCBDMjYuNjQ5MDY4MSwyMC41Mjc0OTcxIDIzLjk4Mzc1MjcsMTYuODU1Mzg5MyAyMy43OTE3MTIsMTYuNTk1MTA3OSBaIE0zMC4zMzc5Mzk3LDI3LjI4MDQ2MjcgQzIyLjkxNjQ2MzgsMjkuNTM2MDc4OCAxNS43OTQ5MDcsMjkuMzczNzgwMSAxNS41Mzk0NDc0LDI5LjM2Mzc4NzMgQzE1LjUzNTI4NTUsMjkuNTIxMDU2MSAxNS41Mjc4ODY2LDI5LjY3MzI5NDkgMTUuNTI3ODg2NiwyOS44MzA2MzA3IEMxNS41Mjc4ODY2LDMzLjU4OTc4OTYgMTYuOTI0ODIyMSwzNy4wMTY4Mzk5IDE5LjIyMDkyMDEsMzkuNjA4MTE5MSBDMTkuMjE1OTY1NCwzOS42MDA1NDA2IDIzLjE2MDQyODgsMzIuNDk2Mjg1NiAzMC45Mzg3Njg2LDI5Ljk0Mjc2NDMgQzMxLjEyNjY0NzQsMjkuODc5MTg2MiAzMS4zMTc4OTUzLDI5LjgyMjI0NzUgMzEuNTA3NDI1NiwyOS43NjcwNTI2IEMzMS4xNDU2NzMsMjguOTM1NDM5NiAzMC43NTA4MjM3LDI4LjEwMjA4MjkgMzAuMzM3OTM5NywyNy4yODA0NjI3IFogTTM5LjQ2MDU2NCwxOC44NDgzMDk2IEMzNi45MjI5NDU0LDE2LjU3NjY2NDkgMzMuNTkwOTM3OCwxNS4xOTg3MzU4IDI5Ljk0MTU3MDksMTUuMTk4NzM1OCBDMjguNzcwNDMzNSwxNS4xOTg3MzU4IDI3LjYzMzkxMjIsMTUuMzQyNjU4NSAyNi41NDUyMTk1LDE1LjYwODcwNzYgQzI2Ljc2MTEwODMsMTUuOTAzMjU5NSAyOS40Njg1MDQ5LDE5LjU0OTQ4IDMxLjkzMzUyMTksMjQuMTQ0NDc3IEMzNy4zNzIxNjMyLDIyLjA3NDU2NTUgMzkuNDI1MDg5LDE4LjkwMTgyNzkgMzkuNDYwNTY0LDE4Ljg0ODMwOTYgWiBNMzIuNDg5ODI1NCwzMi4xOTE3NDA5IEMzMi40NTc3MTk2LDMyLjIwMjUzODUgMzIuNDI1NjEzNywzMi4yMTE4NjA2IDMyLjM5NDIzNDUsMzIuMjIzNTMgQzIzLjg4OTkyNDgsMzUuMjMzMzIxNiAyMS4xMTI3MjcyLDQxLjI5ODkxNTEgMjEuMDgyNjc5Myw0MS4zNjUxMjg2IEMyMy41Mjk2NDYyLDQzLjI5Njk5MzIgMjYuNTk4Nzk1Myw0NC40NjI1MjU2IDI5Ljk0MTU3MDksNDQuNDYyNTI1NiBDMzEuOTM3NjE3OCw0NC40NjI1MjU2IDMzLjgzODkzMjUsNDQuMDQ5OTM4MyAzNS41Njg4MTc3LDQzLjMwMjg5NSBDMzUuMzU1MzczMSw0Mi4wMjQ0MjQyIDM0LjUxODA0NDIsMzcuNTQzMTcwNCAzMi40ODk4MjU0LDMyLjE5MTc0MDkgWiBNMzcuOTk2MDQ3Niw0MS45NjU4NzYgQzQxLjIzMjQ2NDMsMzkuNzQ4Njg4NSA0My41MzEwMDY1LDM2LjIyNzk0NzYgNDQuMTcyMTk5LDMyLjE0OTgyNDkgQzQzLjg3NTUxNywzMi4wNTI3ODExIDM5Ljg0MjkyNzcsMzAuNzU2NzM5MSAzNS4xOTA1NDk4LDMxLjUxMzk3NjMgQzM3LjA4MTE2MjUsMzYuNzg4NDgxNiAzNy44NDkzMjUxLDQxLjA4NDAzMDkgMzcuOTk2MDQ3Niw0MS45NjU4NzYgWiBNMzMuMDQyODkxOSwyNi4zMjQ5MTI2IEMzMy4zNzc0MjcxLDI3LjAyMjcyOTggMzMuNzAxMzkyNSwyNy43MzI5NTQxIDM0LjAwMDUxODgsMjguNDQ2NzMyOSBDMzQuMTA2ODc3NywyOC43MDI3ODkyIDM0LjIxMDcyNjMsMjguOTUzODE1NiAzNC4zMTIwNjQ2LDI5LjIwNDc3NDkgQzM5LjI2MzU2ODcsMjguNTcyMjEyNSA0NC4xNDE3NDQ2LDI5LjYzNjQ3NTkgNDQuMzUwMzAwNiwyOS42Nzk5MzQ0IEM0NC4zMTczMzYsMjYuMjExMTAyMyA0My4wOTUwNjcsMjMuMDI3NTAwMSA0MS4wNzI2NjE2LDIwLjUzNTA3NTUgQzQxLjA0NTQ0NDMsMjAuNTc0MzA4OSAzOC43MzM2ODk4LDIzLjk2NTQxMiAzMy4wNDI4OTE5LDI2LjMyNDkxMjYgWiBNMzMuMDQyODkxOSwyNi4zMjQ5MTI2IiBpZD0iUGF0aCIgZmlsbD0iI0ZGRkZGRiIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPjxwYXRoIGQ9Ik01OS4zMjYyNTc5LDMwIEM1OS4zMjYyNTc5LDQ2LjU2ODU0MzMgNDYuMDk1ODk3Niw2MCAyOS43NzU0MTkzLDYwIEMyMy43MjI1NDA1LDYwIDE4LjA5NDcwNTEsNTguMTUyNTEzNCAxMy40MDkzMjQ0LDU0Ljk4Mjc3NTQgTDQ3LjI2OTU0NTgsNS44MTk0MTEwMyBDNTQuNTgxNDQzOCwxMS4yODA2NTAzIDU5LjMyNjI1NzksMjAuMDc3Nzk3MyA1OS4zMjYyNTc5LDMwIFogTTU5LjMyNjI1NzksMzAiIGlkPSJyZWZsZWMiIGZpbGwtb3BhY2l0eT0iMC4wOCIgZmlsbD0iIzAwMDAwMCIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPjwvc3ZnPgo=); } - -.email { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPg0KICAgIDxwYXRoIGQ9Ik0wLjIyNDU4MDY4OCwzMCBDMC4yMjQ1ODA2ODgsMTMuNDMxNDU2NyAxMy40NTQ5NDEsMCAyOS43NzU0MTkzLDAgQzQ2LjA5NTg5NzYsMCA1OS4zMjYyNTc5LDEzLjQzMTQ1NjcgNTkuMzI2MjU3OSwzMCBDNTkuMzI2MjU3OSw0Ni41Njg1NDMzIDQ2LjA5NTg5NzYsNjAgMjkuNzc1NDE5Myw2MCBDMTMuNDU0OTQxLDYwIDAuMjI0NTgwNjg4LDQ2LjU2ODU0MzMgMC4yMjQ1ODA2ODgsMzAgWiBNMC4yMjQ1ODA2ODgsMzAiIGZpbGw9IiNGRkZGRkYiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCiAgICA8cGF0aCBkPSJNMzUuMDM4NDMyNCwzMS42Mzg0MDA2IEw0Ny4yMTMxMTQ4LDQwLjU3NjQyNjQgTDQ3LjIxMzExNDgsMjAgTDM1LjAzODQzMjQsMzEuNjM4NDAwNiBaIE0xMy43NzA0OTE4LDIwIEwxMy43NzA0OTE4LDQwLjU3NjQyNjQgTDI1Ljk0NDkxMjksMzEuNjM3MTQ5MSBMMTMuNzcwNDkxOCwyMCBaIE0zMC40OTE4MDMzLDM1Ljk4NDQ4OTEgTDI3LjU4NTEwMzcsMzMuMjA2NTIxNyBMMTMuNzcwNDkxOCw0MiBMNDcuMjEzMTE0OCw0MiBMMzMuMzk4MTc2MiwzMy4yMDY1MjE3IEwzMC40OTE4MDMzLDM1Ljk4NDQ4OTEgWiBNNDYuMjA5ODM2MSwyMCBMMTQuNzczNzcwNSwyMCBMMzAuNDkxODAzMywzMi40NTQ5MzA0IEw0Ni4yMDk4MzYxLDIwIFogTTQ2LjIwOTgzNjEsMjAiIGlkPSJTaGFwZSIgZmlsbD0iIzMzMzMzMyIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KICAgIDxwYXRoIGQ9Ik01OS4zMjYyNTc5LDMwIEM1OS4zMjYyNTc5LDQ2LjU2ODU0MzMgNDYuMDk1ODk3Niw2MCAyOS43NzU0MTkzLDYwIEMyMy43MjI1NDA1LDYwIDE4LjA5NDcwNTEsNTguMTUyNTEzNCAxMy40MDkzMjQ0LDU0Ljk4Mjc3NTQgTDQ3LjI2OTU0NTgsNS44MTk0MTEwMyBDNTQuNTgxNDQzOCwxMS4yODA2NTAzIDU5LjMyNjI1NzksMjAuMDc3Nzk3MyA1OS4zMjYyNTc5LDMwIFogTTU5LjMyNjI1NzksMzAiIGlkPSJyZWZsZWMiIGZpbGwtb3BhY2l0eT0iMC4wOCIgZmlsbD0iIzAwMDAwMCIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KPC9zdmc+); } - -.facebook { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPg0KICAgIDxwYXRoIGQ9Ik0wLjExMjI5MDM0NCwzMCBDMC4xMTIyOTAzNDQsMTMuNDMxNDU2NyAxMy4zNDI2NTA2LDAgMjkuNjYzMTI5LDAgQzQ1Ljk4MzYwNzMsMCA1OS4yMTM5Njc2LDEzLjQzMTQ1NjcgNTkuMjEzOTY3NiwzMCBDNTkuMjEzOTY3Niw0Ni41Njg1NDMzIDQ1Ljk4MzYwNzMsNjAgMjkuNjYzMTI5LDYwIEMxMy4zNDI2NTA2LDYwIDAuMTEyMjkwMzQ0LDQ2LjU2ODU0MzMgMC4xMTIyOTAzNDQsMzAgWiBNMC4xMTIyOTAzNDQsMzAiIGZpbGw9IiMzQjU5OTgiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCgk8cGF0aCBkPSJNMzIuMTM0MTQ1Nyw0Ni4zMTk2NzI5IEwzMi4xMzQxNDU3LDI5Ljk5ODA4OTEgTDM2LjU2NTc1NjUsMjkuOTk4MDg5MSBMMzcuMTUzMDQwNiwyNC4zNzM1ODA5IEwzMi4xMzQxNDU3LDI0LjM3MzU4MDkgTDMyLjE0MTY3NSwyMS41NTg0NjA0IEMzMi4xNDE2NzUsMjAuMDkxNTAyIDMyLjI3ODc3MDcsMTkuMzA1NDcyMiAzNC4zNTEyMDYsMTkuMzA1NDcyMiBMMzcuMTIxNjY4NiwxOS4zMDU0NzIyIEwzNy4xMjE2Njg2LDEzLjY4MDMyNzEgTDMyLjY4OTQzMDQsMTMuNjgwMzI3MSBDMjcuMzY1NTk5NSwxMy42ODAzMjcxIDI1LjQ5MTc0OSwxNi40MDg4MTg3IDI1LjQ5MTc0OSwyMC45OTcyODM1IEwyNS40OTE3NDksMjQuMzc0MjE3OSBMMjIuMTczMjE3MywyNC4zNzQyMTc5IEwyMi4xNzMyMTczLDI5Ljk5ODcyNiBMMjUuNDkxNzQ5LDI5Ljk5ODcyNiBMMjUuNDkxNzQ5LDQ2LjMxOTY3MjkgTDMyLjEzNDE0NTcsNDYuMzE5NjcyOSBaIE0zMi4xMzQxNDU3LDQ2LjMxOTY3MjkiIGlkPSJQYXRoIiBmaWxsPSIjRkZGRkZGIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+DQoJPHBhdGggZD0iTTU5LjIxMzk2NzYsMzAgQzU5LjIxMzk2NzYsNDYuNTY4NTQzMyA0NS45ODM2MDczLDYwIDI5LjY2MzEyOSw2MCBDMjMuNjEwMjUwMiw2MCAxNy45ODI0MTQ3LDU4LjE1MjUxMzQgMTMuMjk3MDM0MSw1NC45ODI3NzU0IEw0Ny4xNTcyNTU0LDUuODE5NDExMDMgQzU0LjQ2OTE1MzQsMTEuMjgwNjUwMyA1OS4yMTM5Njc2LDIwLjA3Nzc5NzMgNTkuMjEzOTY3NiwzMCBaIE01OS4yMTM5Njc2LDMwIiBpZD0icmVmbGVjIiBmaWxsLW9wYWNpdHk9IjAuMDgiIGZpbGw9IiMwMDAwMDAiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCjwvc3ZnPg==); } - -.flickr { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPg0KICAgIDxwYXRoIGQ9Ik0wLjMzNjg3MTAzMiwyOS41MDgxOTY3IEMwLjMzNjg3MTAzMiwxMy4yMTEyNjg5IDEzLjU2NzIzMTMsMCAyOS44ODc3MDk3LDAgQzQ2LjIwODE4OCwwIDU5LjQzODU0ODMsMTMuMjExMjY4OSA1OS40Mzg1NDgzLDI5LjUwODE5NjcgQzU5LjQzODU0ODMsNDUuODA1MTI0NiA0Ni4yMDgxODgsNTkuMDE2MzkzNCAyOS44ODc3MDk3LDU5LjAxNjM5MzQgQzEzLjU2NzIzMTMsNTkuMDE2MzkzNCAwLjMzNjg3MTAzMiw0NS44MDUxMjQ2IDAuMzM2ODcxMDMyLDI5LjUwODE5NjcgWiBNMC4zMzY4NzEwMzIsMjkuNTA4MTk2NyIgZmlsbD0iI0ZGMDA4NCIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KICAgIDxwYXRoIGQ9Ik0yMi40OTk5NzM1LDIzLjYwNjU1NzQgQzE4Ljk2Mzc2MzUsMjMuNjA2NTU3NCAxNi4wOTczMTgzLDI2LjQ2ODgwMTcgMTYuMDk3MzE4MywyOS45OTk5NDcyIEMxNi4wOTczMTgzLDMzLjUzMTE0NTUgMTguOTYzNzYzNSwzNi4zOTMzODk4IDIyLjQ5OTk3MzUsMzYuMzkzNDQyNiBDMjYuMDM2MjM2NSwzNi4zOTM0NDI2IDI4LjkwMjY4MTcsMzMuNTMxMTQ1NSAyOC45MDI2ODE3LDI5Ljk5OTk0NzIgQzI4LjkwMjY4MTcsMjYuNDY4ODAxNyAyNi4wMzYyODk0LDIzLjYwNjUwNDUgMjIuNDk5OTczNSwyMy42MDY1NTc0IFogTTIyLjQ5OTk3MzUsMjMuNjA2NTU3NCIgaWQ9IlBhdGgiIGZpbGw9IiNGRkZGRkYiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCiAgICA8cGF0aCBkPSJNMzEuODU3NzY1NiwyOS45OTk5NDcyIEMzMS44NTc3NjU2LDMzLjUzMTE0NTUgMzQuNzI0MjEwOCwzNi4zOTM0NDI2IDM4LjI2MDQyMDgsMzYuMzkzNDQyNiBDNDEuNzk2NzM2NiwzNi4zOTM0NDI2IDQ0LjY2MzEyOSwzMy41MzExNDU1IDQ0LjY2MzEyOSwyOS45OTk5NDcyIEM0NC42NjMxMjksMjYuNDY4ODU0NSA0MS43OTY2ODM3LDIzLjYwNjU1NzQgMzguMjYwNDIwOCwyMy42MDY1NTc0IEMzNC43MjQxNTc5LDIzLjYwNjU1NzQgMzEuODU3NzY1NiwyNi40Njg4NTQ1IDMxLjg1Nzc2NTYsMjkuOTk5OTQ3MiBaIE0zMy44MjY0MDAxLDMwIEMzMy44MjY0MDAxLDI3LjU1NTQ2MDggMzUuODA4MDkwNCwyNS41NzM3NzA1IDM4LjI1MjYyOTYsMjUuNTczNzcwNSBDNDAuNjk3MTY4OCwyNS41NzM3NzA1IDQyLjY3ODg1OTEsMjcuNTU1NDYwOCA0Mi42Nzg4NTkxLDMwIEM0Mi42Nzg4NTkxLDMyLjQ0NDUzOTIgNDAuNjk3MTY4OCwzNC40MjYyMjk1IDM4LjI1MjYyOTYsMzQuNDI2MjI5NSBDMzUuODA4MDkwNCwzNC40MjYyMjk1IDMzLjgyNjQwMDEsMzIuNDQ0NTM5MiAzMy44MjY0MDAxLDMwIFogTTMzLjgyNjQwMDEsMzAiIGlkPSJQYXRoIiBmaWxsPSIjRkZGRkZGIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+DQogICAgPHBhdGggZD0iTTU5LjIxMzk2NzYsMjkuNjg1ODI1MiBDNTkuMjEzOTY3Niw0NS45ODI3NTMxIDQ1Ljk4MzYwNzMsNTkuMTk0MDIxOSAyOS42NjMxMjksNTkuMTk0MDIxOSBDMjMuNjEwMjUwMiw1OS4xOTQwMjE5IDE3Ljk4MjQxNDcsNTcuMzc2ODIyIDEzLjI5NzAzNDEsNTQuMjU5MDQ2OSBMNDcuMTU3MjU1NCw1LjkwMTYzOTM0IEM1NC40NjkxNTM0LDExLjI3MzM1MDEgNTkuMjEzOTY3NiwxOS45MjYyODE2IDU5LjIxMzk2NzYsMjkuNjg1ODI1MiBaIE01OS4yMTM5Njc2LDI5LjY4NTgyNTIiIGlkPSJyZWZsZWMiIGZpbGwtb3BhY2l0eT0iMC4wOCIgZmlsbD0iIzAwMDAwMCIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KPC9zdmc+); } - -.github { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPg0KICAgIDxwYXRoIGQ9Ik0wLjMzNjg3MTAzMiwzMCBDMC4zMzY4NzEwMzIsMTMuNDMxNDU2NyAxMy41NjcyMzEzLDAgMjkuODg3NzA5NywwIEM0Ni4yMDgxODgsMCA1OS40Mzg1NDgzLDEzLjQzMTQ1NjcgNTkuNDM4NTQ4MywzMCBDNTkuNDM4NTQ4Myw0Ni41Njg1NDMzIDQ2LjIwODE4OCw2MCAyOS44ODc3MDk3LDYwIEMxMy41NjcyMzEzLDYwIDAuMzM2ODcxMDMyLDQ2LjU2ODU0MzMgMC4zMzY4NzEwMzIsMzAgWiBNMC4zMzY4NzEwMzIsMzAiIGlkPSJHaXRodWIiIGZpbGw9IiMzMzMzMzMiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCiAgICA8cGF0aCBkPSJNMTguMjE4NDI0NSwzMS45MzU1NTY2IEMxOS42MDY4NTA2LDM0LjQ1MDc5MDIgMjIuMjg0NTI5NSwzNi4wMTU2NzY0IDI2LjgwMDcyODcsMzYuNDQ4NTE3MyBDMjYuMTU2MTAyMywzNi45MzY1MzM1IDI1LjM4MTc4NzcsMzcuODYzMDk4NCAyNS4yNzQ5ODU3LDM4LjkzNDI2MDcgQzI0LjQ2NDQzNDgsMzkuNDU3NDc0OSAyMi44MzQ3NTA2LDM5LjYyOTY2IDIxLjU2NzQzMDMsMzkuMjMxMDY1OSBDMTkuNzkxODQ2OSwzOC42NzE3MDIzIDE5LjExMTkzNzcsMzUuMTY0MjY0MiAxNi40NTMzMzA2LDM1LjY2MzY5NTkgQzE1Ljg3NzM2MjYsMzUuNzcyMTQ0IDE1Ljk5MTc5MzMsMzYuMTUwNzYwOSAxNi40ODk1NjcsMzYuNDcyMjk5OCBDMTcuMzAwMTE3OSwzNi45OTU1MTQxIDE4LjA2Mjk4OTQsMzcuNjUwMDA3NSAxOC42NTEzNTQxLDM5LjA0MzY2IEMxOS4xMDMzNTU0LDQwLjExMzg3MSAyMC4wNTMxMzA0LDQyLjAyNTk4MTMgMjMuMDU2OTM2OSw0Mi4wMjU5ODEzIEMyNC4yNDg5MjM2LDQyLjAyNTk4MTMgMjUuMDg0MjY3OSw0MS44ODMyODY1IDI1LjA4NDI2NzksNDEuODgzMjg2NSBDMjUuMDg0MjY3OSw0MS44ODMyODY1IDI1LjEwNzE1NCw0NC42MTQ0NjQ5IDI1LjEwNzE1NCw0NS42NzYxMTQyIEMyNS4xMDcxNTQsNDYuOTAwNDM1NSAyMy40NTA3NjkzLDQ3LjI0NTc1NjkgMjMuNDUwNzY5Myw0Ny44MzQ2MTA4IEMyMy40NTA3NjkzLDQ4LjA2NzY3OSAyMy45OTkwODMyLDQ4LjA4OTU1ODggMjQuNDM5NjQxNSw0OC4wODk1NTg4IEMyNS4zMTAyNjg1LDQ4LjA4OTU1ODggMjcuMTIyMDg4Myw0Ny4zNjQ2NjkzIDI3LjEyMjA4ODMsNDYuMDkxODMxNyBDMjcuMTIyMDg4Myw0NS4wODA2MDEyIDI3LjEzODI5OTMsNDEuNjgwNjU5OSAyNy4xMzgyOTkzLDQxLjA4NjA5ODIgQzI3LjEzODI5OTMsMzkuNzg1NjczIDI3LjgzNzI4MDMsMzkuMzczNzYwNyAyNy44MzcyODAzLDM5LjM3Mzc2MDcgQzI3LjgzNzI4MDMsMzkuMzczNzYwNyAyNy45MjQwNTcsNDYuMzE1Mzg2OSAyNy42NzA0MDIyLDQ3LjI0NTc1NjkgQzI3LjM3Mjg4MjMsNDguMzM5NzUwNCAyNi44MzYwMTE1LDQ4LjE4NDY4ODcgMjYuODM2MDExNSw0OC42NzI3MDQ5IEMyNi44MzYwMTE1LDQ5LjM5ODU0NTggMjkuMDE2ODcwNCw0OC44NTA1OTc4IDI5LjczOTY5MTEsNDcuMjU3MTcyNSBDMzAuMjk4NDk0NSw0Ni4wMTY2NzkxIDMwLjA1NDM3NTYsMzkuMjA3MjgzNCAzMC4wNTQzNzU2LDM5LjIwNzI4MzQgTDMwLjY1MDM2OSwzOS4xOTQ5MTY1IEMzMC42NTAzNjksMzkuMTk0OTE2NSAzMC42ODM3NDQ2LDQyLjMxMjMyMjIgMzAuNjYzNzE5Miw0My43MzczNjc1IEMzMC42NDI3NDAyLDQ1LjIxMjgzMTcgMzAuNTQyNjEzNCw0Ny4wNzkyNzk3IDMxLjQyMDg2OTIsNDcuOTU5MjMwOSBDMzEuOTk3NzkwNyw0OC41Mzc2MjA1IDMzLjg2ODczMyw0OS41NTI2NTYyIDMzLjg2ODczMyw0OC42MjUxNCBDMzMuODY4NzMzLDQ4LjA4NTc1MzYgMzIuODQzNjI0NSw0Ny42NDI0NDg1IDMyLjg0MzYyNDUsNDYuMTgzMTU2NCBMMzIuODQzNjI0NSwzOS40Njg4OTA1IEMzMy42NjE4MDQyLDM5LjQ2ODg5MDUgMzMuNTM4NzkxMSw0MS42NzY4NTQ3IDMzLjUzODc5MTEsNDEuNjc2ODU0NyBMMzMuNTk4ODY3Myw0NS43Nzg4NTQ0IEMzMy41OTg4NjczLDQ1Ljc3ODg1NDQgMzMuNDE4NjM4OSw0Ny4yNzMzNDQ2IDM1LjIxOTAxNTYsNDcuODk5Mjk5MSBDMzUuODU0MTA2MSw0OC4xMjA5NTE3IDM3LjIxMzkyNDUsNDguMTgwODgzNSAzNy4yNzc4MTUsNDcuODA4OTI1NyBDMzcuMzQxNzA1NSw0Ny40MzYwMTY3IDM1LjY0MDUwMjEsNDYuODgxNDA5NiAzNS42MjUyNDQ2LDQ1LjcyMzY3OTEgQzM1LjYxNTcwODgsNDUuMDE3ODE1NSAzNS42NTY3MTMxLDQ0LjYwNTkwMzIgMzUuNjU2NzEzMSw0MS41Mzc5NjUxIEMzNS42NTY3MTMxLDM4LjQ3MDAyNyAzNS4yNDM4MDg5LDM3LjMzNjA3OSAzMy44MDQ4NDI2LDM2LjQzMjM0NTMgQzM4LjI0NTcwODIsMzUuOTc2NjczMiA0MC45OTM5NTI3LDM0Ljg4MDY4MiA0Mi4zMzM3NDU4LDMxLjk0NTA2OTUgQzQyLjQzODM2MTksMzEuOTQ4NDk2NiA0Mi44NzkxNDkxLDMwLjU3Mzc3NDIgNDIuODIxOTgzNSwzMC41NzQyNDgyIEM0My4xMjIzNjQyLDI5LjQ2NTk4NTMgNDMuMjg0NDc0NCwyOC4xNTUwOTU3IDQzLjMxNjg5NjQsMjYuNjAyNTc2NCBDNDMuMzA5MjY3NywyMi4zOTMwNzk5IDQxLjI4OTU2NTQsMjAuOTA0Mjk3NSA0MC45MDE0NTQ2LDIwLjIwNTA5MyBDNDEuNDczNjA4MiwxNy4wMTgyNDI1IDQwLjgwNjA5NTYsMTUuNTY3NTEyMSA0MC40OTYxNzkxLDE1LjA2OTk4MjkgQzM5LjM1MTg3MTksMTQuNjYzNzc4NCAzNi41MTQ5NDM1LDE2LjExNDUwODggMzQuOTY1MzYwOCwxNy4xMzcxNTQ4IEMzMi40MzgzNDksMTYuMzk5ODk4NCAyNy4wOTgyNDg2LDE2LjQ3MTI0NTggMjUuMDk1NzEwOSwxNy4zMjc0MTQ2IEMyMS40MDA1NTIyLDE0LjY4NzU2MDggMTkuNDQ1Njk0LDE1LjA5MTg2MjggMTkuNDQ1Njk0LDE1LjA5MTg2MjggQzE5LjQ0NTY5NCwxNS4wOTE4NjI4IDE4LjE4MjE4ODEsMTcuMzUxMTk3IDE5LjExMTkzNzcsMjAuNjU2OTU5OCBDMTcuODk2MTExMywyMi4yMDI4MjAxIDE2Ljk5MDIwMTQsMjMuMjk2ODEzNiAxNi45OTAyMDE0LDI2LjE5NjM3MTggQzE2Ljk5MDIwMTQsMjcuODI5NzUxNiAxNy4xODI4MjY0LDI5LjI5MTg5NzYgMTcuNjE3NjYzMiwzMC41Njg1NDA0IEMxNy41NjQzNTc3LDMwLjU2ODQwOTMgMTguMjAwODQ5MywzMS45MzU5Nzc3IDE4LjIxODQyNDUsMzEuOTM1NTU2NiBaIE0xOC4yMTg0MjQ1LDMxLjkzNTU1NjYiIGlkPSJQYXRoIiBmaWxsPSIjRkZGRkZGIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+DQogICAgPHBhdGggZD0iTTU5LjQzODU0ODMsMzAgQzU5LjQzODU0ODMsNDYuNTY4NTQzMyA0Ni4yMDgxODgsNjAgMjkuODg3NzA5Nyw2MCBDMjMuODM0ODMwOCw2MCAxOC4yMDY5OTU0LDU4LjE1MjUxMzQgMTMuNTIxNjE0OCw1NC45ODI3NzU0IEw0Ny4zODE4MzYxLDUuODE5NDExMDMgQzU0LjY5MzczNDEsMTEuMjgwNjUwMyA1OS40Mzg1NDgzLDIwLjA3Nzc5NzMgNTkuNDM4NTQ4MywzMCBaIE01OS40Mzg1NDgzLDMwIiBpZD0icmVmbGVjIiBmaWxsLW9wYWNpdHk9IjAuMDgiIGZpbGw9IiMwMDAwMDAiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCjwvc3ZnPg==); } - -.instagram { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPg0KICAgIDxwYXRoIGQ9Ik0wLjc4NjAzMjQwOCwzMCBDMC43ODYwMzI0MDgsMTMuNDMxNDU2NyAxNC4wMTYzOTI3LDAgMzAuMzM2ODcxLDAgQzQ2LjY1NzM0OTQsMCA1OS44ODc3MDk3LDEzLjQzMTQ1NjcgNTkuODg3NzA5NywzMCBDNTkuODg3NzA5Nyw0Ni41Njg1NDMzIDQ2LjY1NzM0OTQsNjAgMzAuMzM2ODcxLDYwIEMxNC4wMTYzOTI3LDYwIDAuNzg2MDMyNDA4LDQ2LjU2ODU0MzMgMC43ODYwMzI0MDgsMzAgWiBNMC43ODYwMzI0MDgsMzAiIGZpbGw9IiM1MTdGQTQiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCiAgICA8cGF0aCBkPSJNNDIuODI2OTIzMSw0NS4xNzE3OTE3IEM0NC4wNTQxMDkzLDQ0LjkwNzM3NzcgNDUuMDU2ODgzNCw0My44NjgwMTY5IDQ1LjI5Njc0MjYsNDIuNjE0NzcyNyBDNDUuMzIyNDg3Nyw0Mi40NzkyOTg2IDQ1LjM0MTM2NzUsNDIuMzQxNjQ2NCA0NS4zNjMyNTA5LDQyLjIwNTczNjcgTDQ1LjM2MzI1MDksMTcuNzk0MjYzMyBDNDUuMzQyMjI1NywxNy42NjA1MzE2IDQ1LjMyNDIwNDEsMTcuNTI2MzY0MyA0NS4yOTg0NTg5LDE3LjM5MzA2ODMgQzQ1LjAzNTQyOTEsMTYuMDI1Njk0NyA0My45MjQ1MjUzLDE0Ljk2NDU1MzYgNDIuNTYwNDYwNiwxNC43NzUwNjQxIEM0Mi41MDM4MjEzLDE0Ljc2NzY1ODggNDIuNDQ4NDY5MiwxNC43NTUwMjYxIDQyLjM5MjY4OCwxNC43NDUwMDcxIEwxOC4yODEwNTQxLDE0Ljc0NTAwNzEgQzE4LjEzNTU5MzksMTQuNzcxNTc5MiAxNy45ODkyNzU1LDE0Ljc5MTYxNzIgMTcuODQ1OTYwOCwxNC44MjQ3MjM0IEMxNi41MTQ1MDY3LDE1LjEzMjY5ODMgMTUuNTI5MzI1MSwxNi4yMjEyODI3IDE1LjMzOTY2OSwxNy41OTA4MzQzIEMxNS4zMzE5NDU1LDE3LjY0Nzg5OSAxNS4zMjAzNjAxLDE3LjcwNDUyOCAxNS4zMTA0OTEyLDE3Ljc2MTU5MjcgTDE1LjMxMDQ5MTIsNDIuMjM5NzE0MSBDMTUuMzM3OTUyNyw0Mi4zOTQ3OTA2IDE1LjM1OTgzNjEsNDIuNTUxMTczOSAxNS4zOTQxNjMsNDIuNzA1Mzc5MSBDMTUuNjg4NTE2LDQ0LjAyNzAxMzkgMTYuNzk5ODQ4OSw0NS4wNTE1NjM5IDE4LjEyMTg2MzEsNDUuMjI0MDY0NyBDMTguMTg1Nzk3LDQ1LjIzMTkwNTYgMTguMjUwMTU5OSw0NS4yNDQ1MzgzIDE4LjMxNDA5MzcsNDUuMjU0OTkyOSBMNDIuMzU5NjQ4NCw0NS4yNTQ5OTI5IEM0Mi41MTU4MzU3LDQ1LjIyNzk4NTIgNDIuNjcyNDUyMSw0NS4yMDU3NjkyIDQyLjgyNjkyMzEsNDUuMTcxNzkxNyBaIE0xOC41NjE2NzY0LDI3LjE5ODE2OTIgQzE4LjU2MTY3NjQsMzEuNzEzMjQ2MyAxOC41NjEyNDczLDM2LjIyODMyMzUgMTguNTYyMTA1NSw0MC43NDM0MDA2IEMxOC41NjIxMDU1LDQxLjM5Mjg5MjIgMTkuMTE2MDU1Niw0MS45NTEzNDE2IDE5Ljc1NTgyMjksNDEuOTUxMzQxNiBDMjYuODA4MjgxNyw0MS45NTE3NzcyIDMzLjg2MDMxMTQsNDEuOTUxNzc3MiA0MC45MTI3NzAxLDQxLjk1MTM0MTYgQzQxLjU1ODU0NDcsNDEuOTUxMzQxNiA0Mi4xMDk0OTEyLDQxLjM5Mzc2MzQgNDIuMTA5NDkxMiw0MC43Mzk0ODAxIEM0Mi4xMDk5MjAzLDM2LjIyOTYzMDMgNDIuMTA5NDkxMiwzMS43MjAyMTYxIDQyLjEwOTQ5MTIsMjcuMjEwODAxOCBMNDIuMTA5NDkxMiwyNy4xMTcxNDYxIEwzOS4yNDA2MjE3LDI3LjExNzE0NjEgQzM5LjY0NTY3ODksMjguNDMxMzc1NSAzOS43NzAxMTM5LDI5Ljc2ODI1NjUgMzkuNjExMzUyLDMxLjEzMDgzODQgQzM5LjQ1MTczMiwzMi40OTM4NTU5IDM5LjAxOTIxMzMsMzMuNzYyMzQ2NSAzOC4zMTQ2NTM5LDM0LjkzMzY5NjQgQzM3LjYwOTY2NTUsMzYuMTA2MzUzMiAzNi42OTU3MTIxLDM3LjA3Nzc1OTEgMzUuNTc2NjU1NywzNy44NTA5NjMzIEMzMi42NzYwMzM4LDM5Ljg1NjUwMjcgMjguODU3MTY3Nyw0MC4wMzI5MjM5IDI1Ljc3OTMzMzMsMzguMjc5MTY2IEMyNC4yMjM0NjcyLDM3LjM5MzU3NDkgMjMuMDAxODU5MSwzNi4xNTg2MjYyIDIyLjE1MDEyMzIsMzQuNTY3MzUwMSBDMjAuODc5NTk5NCwzMi4xOTI4NTA4IDIwLjY3NjY0MTYsMjkuNzAwNzM3MyAyMS40MjE5NjQyLDI3LjExNDk2ODEgQzIwLjQ3MDI1MTIsMjcuMTE1ODM5MyAxOS41MjI0MDAxLDI3LjExNTgzOTMgMTguNTYxNjc2NCwyNy4xMTU4MzkzIEwxOC41NjE2NzY0LDI3LjE5ODE2OTIgWiBNMjQuMjQ3NDk2LDI5LjkwNjM0NDMgQzI0LjE5OTQzODMsMzMuMzY1NTA3NiAyNi45MjcxMzg1LDM2LjEwNzY2IDMwLjE5NzIwMzUsMzYuMTc5MDk5OCBDMzMuNTk5ODU2MSwzNi4yNTMxNTMxIDM2LjM1MjAxNDIsMzMuNDkzMTQwNyAzNi40MjM2NzE2LDMwLjEzNjM0NTMgQzM2LjQ5NzA0NTMsMjYuNjc0NTY4NCAzMy43NDY2MDM1LDIzLjgxNTIzNzMgMzAuMzM3MDg1NiwyMy44MTc0MTU0IEMyNy4wMDM5NDQ5LDIzLjgxNTIzNzMgMjQuMjk0MjY2NCwyNi41Mzc3ODc1IDI0LjI0NzQ5NiwyOS45MDYzNDQzIFogTTQwLjk1NDM5MTUsMjIuNTY5ODM0IEM0MS41OTA3MjYxLDIyLjU1MzcxNjUgNDIuMTA5MDYyMSwyMi4wMDAwNTg3IDQyLjEwOTA2MjEsMjEuMzUzMTgwOCBDNDIuMTA5NDkxMiwyMC4zOTAwNTE1IDQyLjEwOTQ5MTIsMTkuNDI2NDg2NSA0Mi4xMDkwNjIxLDE4LjQ2MzM1NzIgQzQyLjEwOTA2MjEsMTcuNzcyNDgyOSA0MS41NzA1NTkxLDE3LjIyMjc0NTYgNDAuODkwODg2NywxNy4yMjIzMDk5IEMzOS45NDQ3NTE5LDE3LjIyMTg3NDMgMzguOTk4NjE3MSwxNy4yMjE0Mzg3IDM4LjA1MjA1MzIsMTcuMjIyMzA5OSBDMzcuMzc0MDk3MiwxNy4yMjMxODEyIDM2LjgzMzg3NzksMTcuNzc0MjI1MyAzNi44MzM0NDg4LDE4LjQ2NTA5OTYgQzM2LjgzMzAxOTcsMTkuNDE5OTUyNCAzNi44MzA0NDUyLDIwLjM3NDgwNTIgMzYuODM2ODgxNSwyMS4zMzAwOTM2IEMzNi44Mzc3Mzk2LDIxLjQ3MjUzNzQgMzYuODY0MzQzLDIxLjYyMDY0NDEgMzYuOTA4OTY3OSwyMS43NTU2ODI2IEMzNy4wNzg4ODYsMjIuMjY0MDM3MiAzNy41Mzc1NzksMjIuNTc1OTMyNSAzOC4wOTkyNTI3LDIyLjU3ODU0NjIgQzM4LjU1NTgwMDMsMjIuNTc5NDE3NCAzOS4wMTIzNDc5LDIyLjU3ODExMDYgMzkuNDY5MzI0NSwyMi41NzgxMTA2IEMzOS45NjM2MzE3LDIyLjU3MjQ0NzcgNDAuNDU5NjU1MiwyMi41ODIwMzEgNDAuOTU0MzkxNSwyMi41Njk4MzQgWiBNNDAuOTU0MzkxNSwyMi41Njk4MzQiIGlkPSJQYXRoIiBmaWxsPSIjRkZGRkZGIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+DQogICAgPHBhdGggZD0iTTU5Ljg4NzcwOTcsMzAgQzU5Ljg4NzcwOTcsNDYuNTY4NTQzMyA0Ni42NTczNDk0LDYwIDMwLjMzNjg3MSw2MCBDMjQuMjgzOTkyMiw2MCAxOC42NTYxNTY4LDU4LjE1MjUxMzQgMTMuOTcwNzc2MSw1NC45ODI3NzU0IEw0Ny44MzA5OTc1LDUuODE5NDExMDMgQzU1LjE0Mjg5NTUsMTEuMjgwNjUwMyA1OS44ODc3MDk3LDIwLjA3Nzc5NzMgNTkuODg3NzA5NywzMCBaIE01OS44ODc3MDk3LDMwIiBpZD0icmVmbGVjIiBmaWxsLW9wYWNpdHk9IjAuMDgiIGZpbGw9IiMwMDAwMDAiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCjwvc3ZnPg==); } - -.linkedin { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPg0KICAgIDxwYXRoIGQ9Ik0wLjQ0OTE2MTM3NiwzMCBDMC40NDkxNjEzNzYsMTMuNDMxNDU2NyAxMy42Nzk1MjE3LDAgMzAsMCBDNDYuMzIwNDc4MywwIDU5LjU1MDgzODYsMTMuNDMxNDU2NyA1OS41NTA4Mzg2LDMwIEM1OS41NTA4Mzg2LDQ2LjU2ODU0MzMgNDYuMzIwNDc4Myw2MCAzMCw2MCBDMTMuNjc5NTIxNyw2MCAwLjQ0OTE2MTM3Niw0Ni41Njg1NDMzIDAuNDQ5MTYxMzc2LDMwIFogTTAuNDQ5MTYxMzc2LDMwIiBmaWxsPSIjMDA3QkI2IiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+DQogICAgPHBhdGggZD0iTTIyLjQ2ODAzOTIsMjMuNzA5ODE0NCBMMTUuNzgwODM2NiwyMy43MDk4MTQ0IEwxNS43ODA4MzY2LDQ0LjEzNjk1MzcgTDIyLjQ2ODAzOTIsNDQuMTM2OTUzNyBMMjIuNDY4MDM5MiwyMy43MDk4MTQ0IFogTTIyLjQ2ODAzOTIsMjMuNzA5ODE0NCIgaWQ9IlBhdGgiIGZpbGw9IiNGRkZGRkYiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCiAgICA8cGF0aCBkPSJNMjIuOTA4NDc1MywxNy4zOTA4NzYxIEMyMi44NjUwNzI3LDE1LjM4ODAwODEgMjEuNDU2MjkxNywxMy44NjI1MDQgMTkuMTY4NjQxOCwxMy44NjI1MDQgQzE2Ljg4MDk5MTgsMTMuODYyNTA0IDE1LjM4NTQwNTcsMTUuMzg4MDA4MSAxNS4zODU0MDU3LDE3LjM5MDg3NjEgQzE1LjM4NTQwNTcsMTkuMzUyMjU3OSAxNi44MzY3ODgsMjAuOTIxNjg4NiAxOS4wODE4MzY2LDIwLjkyMTY4ODYgTDE5LjEyNDU3MTQsMjAuOTIxNjg4NiBDMjEuNDU2MjkxNywyMC45MjE2ODg2IDIyLjkwODQ3NTMsMTkuMzUyMjU3OSAyMi45MDg0NzUzLDE3LjM5MDg3NjEgWiBNMjIuOTA4NDc1MywxNy4zOTA4NzYxIiBpZD0iUGF0aCIgZmlsbD0iI0ZGRkZGRiIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KICAgIDxwYXRoIGQ9Ik00Ni41ODQ2NTAyLDMyLjQyNDY1NjMgQzQ2LjU4NDY1MDIsMjYuMTUwMzIyNiA0My4yODU2NTM0LDIzLjIzMDE0NTYgMzguODg1MTY1OCwyMy4yMzAxNDU2IEMzNS4zMzQ3MDExLDIzLjIzMDE0NTYgMzMuNzQ1MDk4MywyNS4yMTI4MTI4IDMyLjg1NzU0ODksMjYuNjAzNjg5NiBMMzIuODU3NTQ4OSwyMy43MTAzNTY3IEwyNi4xNjk1NDQ5LDIzLjcxMDM1NjcgQzI2LjI1NzY4NTYsMjUuNjI3MTMzOCAyNi4xNjk1NDQ5LDQ0LjEzNzQ5NiAyNi4xNjk1NDQ5LDQ0LjEzNzQ5NiBMMzIuODU3NTQ4OSw0NC4xMzc0OTYgTDMyLjg1NzU0ODksMzIuNzI5Mjk2MSBDMzIuODU3NTQ4OSwzMi4xMTg3OTYzIDMyLjkwMDk1MTQsMzEuNTA5Nzg3NyAzMy4wNzc3NjY5LDMxLjA3MjY4OTggQzMzLjU2MTA3MTMsMjkuODUzMDQ1OCAzNC42NjE0OTM3LDI4LjU5MDI4ODUgMzYuNTA4OTc0NywyOC41OTAyODg1IEMzOC45Mjk3NzAzLDI4LjU5MDI4ODUgMzkuODk3NDQ3NiwzMC40NjM0MTAxIDM5Ljg5NzQ0NzYsMzMuMjA4NDIyNiBMMzkuODk3NDQ3Niw0NC4xMzY5NTM3IEw0Ni41ODQzODMyLDQ0LjEzNjk1MzcgTDQ2LjU4NDY1MDIsMzIuNDI0NjU2MyBaIE00Ni41ODQ2NTAyLDMyLjQyNDY1NjMiIGlkPSJQYXRoIiBmaWxsPSIjRkZGRkZGIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+DQogICAgPHBhdGggZD0iTTU5LjU1MDgzODYsMzAgQzU5LjU1MDgzODYsNDYuNTY4NTQzMyA0Ni4zMjA0NzgzLDYwIDMwLDYwIEMyMy45NDcxMjEyLDYwIDE4LjMxOTI4NTgsNTguMTUyNTEzNCAxMy42MzM5MDUxLDU0Ljk4Mjc3NTQgTDQ3LjQ5NDEyNjQsNS44MTk0MTEwMyBDNTQuODA2MDI0NSwxMS4yODA2NTAzIDU5LjU1MDgzODYsMjAuMDc3Nzk3MyA1OS41NTA4Mzg2LDMwIFogTTU5LjU1MDgzODYsMzAiIGlkPSJyZWZsZWMiIGZpbGwtb3BhY2l0eT0iMC4wOCIgZmlsbD0iIzAwMDAwMCIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KPC9zdmc+); } - -.pinterest { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPg0KICAgIDxwYXRoIGQ9Ik0wLjg4MzgzMzY3NSwzMCBDMC44ODM4MzM2NzUsMTMuNDMxNDU2NyAxMy45MDA4MDExLDAgMjkuOTU4MDQ1OSwwIEM0Ni4wMTUyOTA3LDAgNTkuMDMyMjU4MSwxMy40MzE0NTY3IDU5LjAzMjI1ODEsMzAgQzU5LjAzMjI1ODEsNDYuNTY4NTQzMyA0Ni4wMTUyOTA3LDYwIDI5Ljk1ODA0NTksNjAgQzEzLjkwMDgwMTEsNjAgMC44ODM4MzM2NzUsNDYuNTY4NTQzMyAwLjg4MzgzMzY3NSwzMCBaIE0wLjg4MzgzMzY3NSwzMCIgZmlsbD0iI0NCMjAyNyIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KICAgIDxwYXRoIGQ9Ik0xNy41ODY5MTY1LDI1LjY1NjQ4OTQgQzE3LjU4NjkxNjUsMjguOTU5MDYxOCAxOC43OTg2ODUxLDMxLjg5NzE2OTggMjEuMzk3Njk0MSwzMi45OTIwNTU5IEMyMS44MjM4Njk2LDMzLjE3MTgzNTYgMjIuMjA1NjA4NywzMi45OTgyNDA1IDIyLjMyOTIwMzcsMzIuNTExMzYzOCBDMjIuNDE1MTgyOSwzMi4xNzQ0MTAxIDIyLjYxODU1NjYsMzEuMzI0MzQ4NSAyMi43MDkyODk0LDMwLjk3MDMzMzkgQzIyLjgzMzkxNzksMzAuNDg4Nzg4OCAyMi43ODU1NTQ2LDMwLjMxOTg4NTQgMjIuNDQxNjM4LDI5LjkwMDE4NjIgQzIxLjY5MjIxNCwyOC45ODgwNjU0IDIxLjIxMzMzNSwyNy44MDcyMzQ4IDIxLjIxMzMzNSwyNi4xMzQ2MjI0IEMyMS4yMTMzMzUsMjEuMjgyMDYzMSAyNC43MzE4NjYyLDE2LjkzNzkyIDMwLjM3NTQ4NzcsMTYuOTM3OTIgQzM1LjM3MjgxODgsMTYuOTM3OTIgMzguMTE4MzY0NCwyMC4wODg2NSAzOC4xMTgzNjQ0LDI0LjI5NjUxODggQzM4LjExODM2NDQsMjkuODMzMDA4NyAzNS43NDM4MTA2LDM0LjUwNTc4ODIgMzIuMjE4NjY1NiwzNC41MDU3ODgyIEMzMC4yNzE5NDA3LDM0LjUwNTc4ODIgMjguODE0NjM1NSwzMi44NDQ0Nzg3IDI5LjI4MTczMzcsMzAuODA2OTc1NCBDMjkuODQxMDExNSwyOC4zNzQ1MTEyIDMwLjkyNDQzMTQsMjUuNzQ5MjU4MyAzMC45MjQ0MzE0LDIzLjk5MzQ3MzggQzMwLjkyNDQzMTQsMjIuNDIxNzM0MiAzMC4xMDY4MDI4LDIxLjExMDgxMzkgMjguNDE0NzA4NCwyMS4xMTA4MTM5IEMyNi40MjQ1ODA2LDIxLjExMDgxMzkgMjQuODI1OTA1OCwyMy4yMzUxMTQ4IDI0LjgyNTkwNTgsMjYuMDgwODgwNCBDMjQuODI1OTA1OCwyNy44OTMzOTI1IDI1LjQxOTQ5MjYsMjkuMTE5MjIxNCAyNS40MTk0OTI2LDI5LjExOTIyMTQgQzI1LjQxOTQ5MjYsMjkuMTE5MjIxNCAyMy4zODI4NjE2LDM4LjAyMzExNTMgMjMuMDI1OTI0MiwzOS41ODI0ODU2IEMyMi4zMTQ5NDI4LDQyLjY4ODAwNDEgMjIuOTE5MDcwMyw0Ni40OTQ5NDA3IDIyLjk3MDEyMDQsNDYuODc5NDUxOCBDMjMuMDAwMDg5MSw0Ny4xMDcyMTU0IDIzLjI4Mzg2MTYsNDcuMTYxMzgzOSAyMy40MTI0MTcsNDYuOTg5MjgxNiBDMjMuNTk1OTQ5NCw0Ni43NDIxMTEyIDI1Ljk2NjE2MjksNDMuNzIyNzUwNCAyNi43NzIwMTA3LDQwLjcwNTczNTYgQzI2Ljk5OTk3OTUsMzkuODUxNDA4OCAyOC4wODA5MTkyLDM1LjQyNzcxOSAyOC4wODA5MTkyLDM1LjQyNzcxOSBDMjguNzI3NDE2MiwzNi43MDAyNTIzIDMwLjYxNzA5NzMsMzcuODIxMTU2NCAzMi42MjY2NTMyLDM3LjgyMTE1NjQgQzM4LjYwODgxNzYsMzcuODIxMTU2NCA0Mi42Njc0MDUyLDMyLjE5MzgxNjkgNDIuNjY3NDA1MiwyNC42NjE0MDk4IEM0Mi42Njc0MDUyLDE4Ljk2NTgyNjYgMzcuOTkyMDgyNSwxMy42NjEzNjU2IDMwLjg4NjE5NTUsMTMuNjYxMzY1NiBDMjIuMDQ0ODExMiwxMy42NjEzNjU2IDE3LjU4NjkxNjUsMjAuMjAyMTA1MyAxNy41ODY5MTY1LDI1LjY1NjQ4OTQgWiBNMTcuNTg2OTE2NSwyNS42NTY0ODk0IiBpZD0iUGF0aCIgZmlsbD0iI0ZGRkZGRiIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KICAgIDxwYXRoIGQ9Ik01OS4wMzIyNTgxLDMwIEM1OS4wMzIyNTgxLDQ2LjU2ODU0MzMgNDYuMDE1MjkwNyw2MCAyOS45NTgwNDU5LDYwIEMyNC4wMDI3OTQxLDYwIDE4LjQ2NTczMDIsNTguMTUyNTEzNCAxMy44NTU5MjAzLDU0Ljk4Mjc3NTQgTDQ3LjE3MDAwOSw1LjgxOTQxMTAzIEM1NC4zNjM5NzMyLDExLjI4MDY1MDMgNTkuMDMyMjU4MSwyMC4wNzc3OTczIDU5LjAzMjI1ODEsMzAgWiBNNTkuMDMyMjU4MSwzMCIgaWQ9InJlZmxlYyIgZmlsbC1vcGFjaXR5PSIwLjA4IiBmaWxsPSIjMDAwMDAwIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+DQo8L3N2Zz4=); } - -.rss { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPg0KICAgIDxwYXRoIGQ9Ik0wLjExMjI5MDM0NCwzMCBDMC4xMTIyOTAzNDQsMTMuNDMxNDU2NyAxMy4zNDI2NTA2LDAgMjkuNjYzMTI5LDAgQzQ1Ljk4MzYwNzMsMCA1OS4yMTM5Njc2LDEzLjQzMTQ1NjcgNTkuMjEzOTY3NiwzMCBDNTkuMjEzOTY3Niw0Ni41Njg1NDMzIDQ1Ljk4MzYwNzMsNjAgMjkuNjYzMTI5LDYwIEMxMy4zNDI2NTA2LDYwIDAuMTEyMjkwMzQ0LDQ2LjU2ODU0MzMgMC4xMTIyOTAzNDQsMzAgWiBNMC4xMTIyOTAzNDQsMzAiIGZpbGw9IiNFMThGMDAiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCiAgICA8cGF0aCBkPSJNMjYuNTE0Nzc1NSwzOC43NTcxMjk2IEMyNi41MTQ3NzU1LDM2LjgxOTI5MzQgMjQuOTU3NTAyMSwzNS4yMjkxNjU2IDIzLjAyOTcwNDcsMzUuMjI5MTY1NiBDMjEuMTA5NDMzMSwzNS4yMjkxNjU2IDE5LjU0OTI3NzUsMzYuODE5MjkzNCAxOS41NDkyNzc1LDM4Ljc1NzEyOTYgQzE5LjU0OTI3NzUsNDAuNzA1MjA2OCAyMS4xMDk0MzMxLDQyLjI3NzYxNiAyMy4wMjk3MDQ3LDQyLjI3NzYxNiBDMjQuOTU3NDIyLDQyLjI3NzYxNiAyNi41MTQ2OTU0LDQwLjcwNTI4ODEgMjYuNTE0Nzc1NSwzOC43NTcxMjk2IFogTTM2LjgwNzU0MzMsNDIuMjcwMDU3MSBDMzYuODA3NTQzMywzMi42MDU1ODQ3IDI5LjA2MjY0OCwyNC43NDQyNzA1IDE5LjU0OTI3NzUsMjQuNzQ0MjcwNSBMMTkuNTQ5Mjc3NSwyOS44MzE4MDE3IEMyMi44MTI4OTg2LDI5LjgzMTgwMTcgMjUuODgxNzMwMywzMS4xMjcyMTQ1IDI4LjE5MjA2MDksMzMuNDc0MDQyOSBDMzAuNDk5OTg5NSwzNS44MTU5MTMzIDMxLjc3MzI4NTMsMzguOTQ1NDUxMyAzMS43NzMyODUzLDQyLjI3MDEzODQgTDM2LjgwNzU0MzMsNDIuMjcwMDU3MSBaIE00NS42ODcxNDgyLDQyLjI2NzUzNzUgQzQ1LjY4NzE0ODIsMjcuNjM0Njg3OCAzMy45NjA0MDE0LDE1LjcyMjM4NCAxOS41NDkyNzc1LDE1LjcyMjM4NCBMMTkuNTQ5Mjc3NSwyMC44MTIzNTM1IEMzMS4xODY5MTYsMjAuODEyMzUzNSA0MC42NjA0OTYsMzAuNDM4ODY5IDQwLjY2MDQ5Niw0Mi4yNjczNzQ5IEw0NS42ODcxNDgyLDQyLjI2NzUzNzUgWiBNNDUuNjg3MTQ4Miw0Mi4yNjc1Mzc1IiBpZD0iUGF0aCIgZmlsbD0iI0ZGRkZGRiIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KICAgIDxwYXRoIGQ9Ik01OS4yMTM5Njc2LDMwIEM1OS4yMTM5Njc2LDQ2LjU2ODU0MzMgNDUuOTgzNjA3Myw2MCAyOS42NjMxMjksNjAgQzIzLjYxMDI1MDIsNjAgMTcuOTgyNDE0Nyw1OC4xNTI1MTM0IDEzLjI5NzAzNDEsNTQuOTgyNzc1NCBMNDcuMTU3MjU1NCw1LjgxOTQxMTAzIEM1NC40NjkxNTM0LDExLjI4MDY1MDMgNTkuMjEzOTY3NiwyMC4wNzc3OTczIDU5LjIxMzk2NzYsMzAgWiBNNTkuMjEzOTY3NiwzMCIgaWQ9InJlZmxlYyIgZmlsbC1vcGFjaXR5PSIwLjA4IiBmaWxsPSIjMDAwMDAwIiBza2V0Y2g6dHlwZT0iTVNTaGFwZUdyb3VwIj48L3BhdGg+DQo8L3N2Zz4=); } - -.stackoverflow { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+DQogIDxwYXRoIGZpbGw9IiM5MTkxOTEiIGQ9Im03LjI5NDA3NSwyMy4xMTAxODhsMi4zODU4NjQsMC4wMjMyMzdsLTAuMDgzMTc3LDEwLjU1MjY3OWwxNS44NzgyMjEsMGwwLC0xMC41MTQ1MDVsMi41MTQwNSwwbDAsMTMuMDc3NDFsLTIwLjcwNzg0MywwbDAuMDEyODksLTEzLjEzODgxNWwtMC4wMDAwMDUsLTAuMDAwMDA2eiIvPg0KICA8cmVjdCBmaWxsPSIjOTE5MTkxIiB5PSIyOS4zNDg4NjIiIHg9IjExLjE4NDU4MiIgaGVpZ2h0PSIyLjYyODYyNiIgd2lkdGg9IjEyLjQzNzkzOCIvPg0KICA8cmVjdCBmaWxsPSIjYTc4YjY4IiB0cmFuc2Zvcm09Im1hdHJpeCgwLjUzOTU3MzM0MTAzMDQ3MTcsMC4wNTE3MjY2OTg0ODM5OTIwNCwtMC4wNTIwNzU4NjQ3ODAwMzI1MiwwLjUzNTk1NTUyNjQxMDg2NjUsMy42NjExNDE2NjQ4Njc4MzI1LDEuNDA2NzI1NTcyNzY2NjYxNCkgIiB5PSI0MS41MjQzNDciIHg9IjE4LjQ5MTI1IiBoZWlnaHQ9IjUuMDAzOTIzIiB3aWR0aD0iMjMuMDY2ODY0Ii8+DQogIDxyZWN0IGZpbGw9IiNjMTk2NTMiIHRyYW5zZm9ybT0ibWF0cml4KDAuNTIxNjk5ODY3NjIzODk1OCwwLjE0NjI1NTg2MDkyMjc4OTkyLC0wLjE0NzI0MzExOTMxNjAyNDIsMC41MTgyMDE4OTM4Mjc1NDE2LDMuNjYxMTQxNjY0ODY3ODMyNSwxLjQwNjcyNTU3Mjc2NjY2MTQpICIgeT0iMjcuNDM4NTE1IiB4PSIyNC45MDcwNzYiIGhlaWdodD0iNS4wMDM5MjMiIHdpZHRoPSIyMy4wNjY4NjQiLz4NCiAgPHJlY3QgZmlsbD0iI2Q0OGMyOCIgdHJhbnNmb3JtPSJtYXRyaXgoMC40NjQwMDkwMjQ0MjYxMjE2LDAuMjc4MzgzMDczOTI4ODQ3NSwtMC4yODAyNjIyMTkzMTUxMzUzLDAuNDYwODk3ODY1MDk5NzY5OTMsMy42NjExNDE2NjQ4Njc4MzI1LDEuNDA2NzI1NTcyNzY2NjYxNCkgIiB5PSI3LjQ2MTA5MiIgeD0iMzAuMDg5Mzk2IiBoZWlnaHQ9IjUuMDAzOTIzIiB3aWR0aD0iMjMuMDY2ODY0Ii8+DQogIDxyZWN0IGZpbGw9IiNmZTg5MDgiIHRyYW5zZm9ybT0ibWF0cml4KDAuMzE1NzIyMjcwMDE0MzMwNCwwLjQzNzY5MzU2MDg5OTk2MDIsLTAuNDQwNjQ4MDg2MjAxOTY2NCwwLjMxMzYwNTM2NjYwNjg4NzM3LDMuNjYxMTQxNjY0ODY3ODMyNSwxLjQwNjcyNTU3Mjc2NjY2MTQpICIgeT0iLTIwLjM0MzgwMyIgeD0iMjguNDg4MzAzIiBoZWlnaHQ9IjUuMDAzOTIzIiB3aWR0aD0iMjMuMDY2ODY0Ii8+DQogIDxyZWN0IGZpbGw9IiNmZjdhMTUiIHRyYW5zZm9ybT0ibWF0cml4KDAuMDg5MzQwMjMwNDgyMTY3NiwwLjUzMTA4MjgzMzM4NDMxNzksLTAuNTM0NjY3NzU2MjgyMTI5OSwwLjA4ODc0MTIwODMxNTI0NTg1LDMuNjYxMTQxNjY0ODY3ODMyNSwxLjQwNjcyNTU3Mjc2NjY2MTQpICIgeT0iLTQ3LjA0NTkyNyIgeD0iMTQuMTU4MjAzIiBoZWlnaHQ9IjUuMDAzOTIzIiB3aWR0aD0iMjMuMDY2ODY0Ii8+DQo8L3N2Zz4NCg==); } - -.twitter { background-image: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNjAgNjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiPg0KICAgIDxwYXRoIGQ9Ik0wLDMwIEMwLDEzLjQzMTQ1NjcgMTMuNDUwODY2MywwIDMwLjA0MzM1MjYsMCBDNDYuNjM1ODM4OSwwIDYwLjA4NjcwNTIsMTMuNDMxNDU2NyA2MC4wODY3MDUyLDMwIEM2MC4wODY3MDUyLDQ2LjU2ODU0MzMgNDYuNjM1ODM4OSw2MCAzMC4wNDMzNTI2LDYwIEMxMy40NTA4NjYzLDYwIDAsNDYuNTY4NTQzMyAwLDMwIFogTTAsMzAiIGZpbGw9IiM0MDk5RkYiIHNrZXRjaDp0eXBlPSJNU1NoYXBlR3JvdXAiPjwvcGF0aD4NCiAgICA8cGF0aCBkPSJNMjkuMjk5NzY3NSwyMy44ODc5Nzc2IEwyOS4zNjI3MjA2LDI0LjkyNjA0NTMgTDI4LjMxMzUwMTYsMjQuNzk4OTM1IEMyNC40OTQzNDQ1LDI0LjMxMTY3ODcgMjEuMTU3ODI4MSwyMi42NTkyNDQ0IDE4LjMyNDkzNjgsMTkuODg0MDAyMyBMMTYuOTM5OTY3NywxOC41MDY5NzM3IEwxNi41ODMyMzMzLDE5LjUyMzg1NjMgQzE1LjgyNzc5NTYsMjEuNzkwNjU3MiAxNi4zMTA0MzYzLDI0LjE4NDU2ODQgMTcuODg0MjY0OCwyNS43OTQ2MzI1IEMxOC43MjM2NCwyNi42ODQ0MDQ4IDE4LjUzNDc4MDYsMjYuODExNTE1MiAxNy4wODY4NTg0LDI2LjI4MTg4ODggQzE2LjU4MzIzMzMsMjYuMTEyNDA4MyAxNi4xNDI1NjEzLDI1Ljk4NTI5OCAxNi4xMDA1OTI1LDI2LjA0ODg1MzIgQzE1Ljk1MzcwMTksMjYuMTk3MTQ4NiAxNi40NTczMjcsMjguMTI0OTg4NSAxNi44NTYwMzAyLDI4Ljg4NzY1MDUgQzE3LjQwMTYyNDEsMjkuOTQ2OTAzMyAxOC41MTM3OTYyLDMwLjk4NDk3MDkgMTkuNzMwODkwMiwzMS41OTkzMzc1IEwyMC43NTkxMjQ4LDMyLjA4NjU5MzggTDE5LjU0MjAzMDgsMzIuMTA3Nzc4OCBDMTguMzY2OTA1NSwzMi4xMDc3Nzg4IDE4LjMyNDkzNjgsMzIuMTI4OTYzOSAxOC40NTA4NDMxLDMyLjU3Mzg1IEMxOC44NzA1MzA3LDMzLjk1MDg3ODYgMjAuNTI4Mjk2NywzNS40MTI2NDc0IDIyLjM3NDkyMjEsMzYuMDQ4MTk5IEwyMy42NzU5NTM2LDM2LjQ5MzA4NTIgTDIyLjU0Mjc5NzEsMzcuMTcxMDA2OSBDMjAuODY0MDQ2NywzOC4xNDU1MTk0IDE4Ljg5MTUxNSwzOC42OTYzMzA5IDE2LjkxODk4MzMsMzguNzM4NzAxIEMxNS45NzQ2ODYyLDM4Ljc1OTg4NiAxNS4xOTgyNjQyLDM4Ljg0NDYyNjIgMTUuMTk4MjY0MiwzOC45MDgxODE0IEMxNS4xOTgyNjQyLDM5LjEyMDAzMTkgMTcuNzU4MzU4NSw0MC4zMDYzOTUgMTkuMjQ4MjQ5NSw0MC43NzI0NjYyIEMyMy43MTc5MjI0LDQyLjE0OTQ5NDggMjkuMDI2OTcwNSw0MS41NTYzMTMyIDMzLjAxNDAwMjcsMzkuMjA0NzcyMiBDMzUuODQ2ODk0LDM3LjUzMTE1MjggMzguNjc5Nzg1MywzNC4yMDUwOTkzIDQwLjAwMTgwMTIsMzAuOTg0OTcwOSBDNDAuNzE1MjcwMSwyOS4yNjg5ODE1IDQxLjQyODczOSwyNi4xMzM1OTM0IDQxLjQyODczOSwyNC42Mjk0NTQ1IEM0MS40Mjg3MzksMjMuNjU0OTQyIDQxLjQ5MTY5MjIsMjMuNTI3ODMxNyA0Mi42NjY4MTc0LDIyLjM2MjY1MzcgQzQzLjM1OTMwMiwyMS42ODQ3MzE5IDQ0LjAwOTgxNzgsMjAuOTQzMjU1IDQ0LjEzNTcyNCwyMC43MzE0MDQ0IEM0NC4zNDU1Njc4LDIwLjMyODg4ODQgNDQuMzI0NTgzNSwyMC4zMjg4ODg0IDQzLjI1NDM4MDEsMjAuNjg5MDM0MyBDNDEuNDcwNzA3OCwyMS4zMjQ1ODYgNDEuMjE4ODk1MiwyMS4yMzk4NDU4IDQyLjEwMDIzOTIsMjAuMjg2NTE4MyBDNDIuNzUwNzU1LDE5LjYwODU5NjUgNDMuNTI3MTc3LDE4LjM3OTg2MzQgNDMuNTI3MTc3LDE4LjAxOTcxNzQgQzQzLjUyNzE3NywxNy45NTYxNjIzIDQzLjIxMjQxMTMsMTguMDYyMDg3NiA0Mi44NTU2NzY5LDE4LjI1Mjc1MyBDNDIuNDc3OTU4LDE4LjQ2NDYwMzYgNDEuNjM4NTgyOCwxOC43ODIzNzk0IDQxLjAwOTA1MTQsMTguOTczMDQ0OSBMMzkuODc1ODk0OSwxOS4zMzMxOTA4IEwzOC44NDc2NjAzLDE4LjYzNDA4NCBDMzguMjgxMDgyLDE4LjI1Mjc1MyAzNy40ODM2NzU2LDE3LjgyOTA1MiAzNy4wNjM5ODgsMTcuNzAxOTQxNiBDMzUuOTkzNzg0NiwxNy40MDUzNTA5IDM0LjM1NzAwMywxNy40NDc3MjEgMzMuMzkxNzIxNSwxNy43ODY2ODE4IEMzMC43Njg2NzQsMTguNzQwMDA5MyAyOS4xMTA5MDgsMjEuMTk3NDc1NyAyOS4yOTk3Njc1LDIzLjg4Nzk3NzYgWiBNMjkuMjk5NzY3NSwyMy44ODc5Nzc2IiBpZD0iUGF0aCIgZmlsbD0iI0ZGRkZGRiIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KICAgIDxwYXRoIGQ9Ik02MC4wODY3MDUyLDMwIEM2MC4wODY3MDUyLDQ2LjU2ODU0MzMgNDYuNjM1ODM4OSw2MCAzMC4wNDMzNTI2LDYwIEMyMy44ODk1OTI1LDYwIDE4LjE2Nzk1OTgsNTguMTUyNTEzNCAxMy40MDQ0ODk1LDU0Ljk4Mjc3NTQgTDQ3LjgyOTA0NzgsNS44MTk0MTEwMyBDNTUuMjYyODEwOCwxMS4yODA2NTAzIDYwLjA4NjcwNTIsMjAuMDc3Nzk3MyA2MC4wODY3MDUyLDMwIFogTTYwLjA4NjcwNTIsMzAiIGlkPSJyZWZsZWMiIGZpbGwtb3BhY2l0eT0iMC4wOCIgZmlsbD0iIzAwMDAwMCIgc2tldGNoOnR5cGU9Ik1TU2hhcGVHcm91cCI+PC9wYXRoPg0KPC9zdmc+); } diff --git a/_site/about/index.html b/about/index.html similarity index 100% rename from _site/about/index.html rename to about/index.html diff --git a/about/index.md b/about/index.md deleted file mode 100644 index b4168f4..0000000 --- a/about/index.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -layout: default ---- - -# About - -## Me - -I'm a Senior Security Engineer at iSEC Partners. At iSEC, I specialize in -web application and web services security, mobile application security, cloud -environment security, architecture and design review, and client/server -testing. iSEC Partners' research blog is available at https://isecpartners.github.io. - -## Contact - -* Email: l01cd3v[at]gmail[dot]com -* Work: loic[at]isecpartners[dot]com -* PPG: https://pgp.mit.edu/pks/lookup?op=get&search=0x1A9A639A8DB9603E diff --git a/_site/archives/index.html b/archives/index.html similarity index 100% rename from _site/archives/index.html rename to archives/index.html diff --git a/archives/index.md b/archives/index.md deleted file mode 100644 index 3fb29e2..0000000 --- a/archives/index.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -layout: archives -title: Blog Posts ---- - -
-{% for post in site.posts %} -
{{ post.title }}
-
{{ post.date | date_to_string }}
-{% endfor %} -
diff --git a/_site/aws/2015/02/22/announcing_the_aws_blog_post_series.html b/aws/2015/02/22/announcing_the_aws_blog_post_series.html similarity index 100% rename from _site/aws/2015/02/22/announcing_the_aws_blog_post_series.html rename to aws/2015/02/22/announcing_the_aws_blog_post_series.html diff --git a/_site/aws/2015/02/23/do_not_use_your_root_account.html b/aws/2015/02/23/do_not_use_your_root_account.html similarity index 100% rename from _site/aws/2015/02/23/do_not_use_your_root_account.html rename to aws/2015/02/23/do_not_use_your_root_account.html diff --git a/_site/aws/2015/02/24/iam_user_management.html b/aws/2015/02/24/iam_user_management.html similarity index 100% rename from _site/aws/2015/02/24/iam_user_management.html rename to aws/2015/02/24/iam_user_management.html diff --git a/_site/aws/2015/04/02/use_and_enforce_mfa.html b/aws/2015/04/02/use_and_enforce_mfa.html similarity index 100% rename from _site/aws/2015/04/02/use_and_enforce_mfa.html rename to aws/2015/04/02/use_and_enforce_mfa.html diff --git a/_site/aws/2015/04/03/session_management_tools.html b/aws/2015/04/03/session_management_tools.html similarity index 100% rename from _site/aws/2015/04/03/session_management_tools.html rename to aws/2015/04/03/session_management_tools.html diff --git a/_site/aws/2015/06/09/iam_user_management_2.html b/aws/2015/06/09/iam_user_management_2.html similarity index 100% rename from _site/aws/2015/06/09/iam_user_management_2.html rename to aws/2015/06/09/iam_user_management_2.html diff --git a/_site/aws/2015/08/03/boto3_opinel_scout_recipes.html b/aws/2015/08/03/boto3_opinel_scout_recipes.html similarity index 100% rename from _site/aws/2015/08/03/boto3_opinel_scout_recipes.html rename to aws/2015/08/03/boto3_opinel_scout_recipes.html diff --git a/_site/aws/2015/08/06/scout2_supports_redshift.html b/aws/2015/08/06/scout2_supports_redshift.html similarity index 100% rename from _site/aws/2015/08/06/scout2_supports_redshift.html rename to aws/2015/08/06/scout2_supports_redshift.html diff --git a/_site/aws/2015/11/17/ip_ranges_management.html b/aws/2015/11/17/ip_ranges_management.html similarity index 100% rename from _site/aws/2015/11/17/ip_ranges_management.html rename to aws/2015/11/17/ip_ranges_management.html diff --git a/_site/aws/2016/08/08/blackhat_slides.html b/aws/2016/08/08/blackhat_slides.html similarity index 100% rename from _site/aws/2016/08/08/blackhat_slides.html rename to aws/2016/08/08/blackhat_slides.html diff --git a/_site/categories/aws/index.html b/categories/aws/index.html similarity index 100% rename from _site/categories/aws/index.html rename to categories/aws/index.html diff --git a/_site/categories/index.html b/categories/index.html similarity index 100% rename from _site/categories/index.html rename to categories/index.html diff --git a/categories/index.md b/categories/index.md deleted file mode 100644 index fe93bc6..0000000 --- a/categories/index.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -layout: archives -title: Blog Posts ---- - -{% for category in site.categories %} -

{{ category | first }}

-
- {% for posts in category %} - {% for post in posts %} - {% if post.title %} -
{{ post.title }}
-
{{ post.date | date_to_string }}
- {% endif %} - {% endfor %} - {% endfor %} -
-{% endfor %} diff --git a/feed.xml b/feed.xml index 055c4dd..627e769 100644 --- a/feed.xml +++ b/feed.xml @@ -1,21 +1,1084 @@ ---- -layout: null ---- - {{ site.name | xml_escape }} - {{ site.description | xml_escape }} - {{ site.url }} - - {% for post in site.posts limit:10 %} - - {{ post.title | xml_escape }} - {{ post.content | xml_escape }} - {{ post.date | date: "%a, %d %b %Y %H:%M:%S %z" }} - {{ site.url }}{{ post.url }} - {{ site.url }}{{ post.url }} - - {% endfor %} + l01cd3v.github.io + + http://l01cd3v.github.io + + + + Post Black Hat US 2016 blog post + <p>Last Tuesday (August 3rd), I presented &quot;Access Keys Will Kill You Before You +Kill The Password&quot; at Black Hat US 2016. The summary is on the <a +target="_blank" +href="https://www.blackhat.com/us-16/briefings.html#access-keys-will-kill-you-before-you-kill-the-password">Black +Hat website</a> and the updated slide deck is available <a +href="slides/us-16-Simon-Access-Keys-Will-Kill-You-Before-You-Kill-The-Password.pdf">Here</a>. +This presentation aimed at highlighting risks associated with usage of AWS API +access keys in environments that do not enforce MFA-protected API access, and +documented strategies and IAM policies to help address these risks.</p> + +<p>On Wednesday (August 4th), I presented Scout2 at Black Hat Arsenal. During two +hours, I had the opportunity to demo Scout2 and meet users of the tool who +shared valuable feedback with me. I look forward to implementing some of the +features discussed during this event, including adding support for ECS and +finishing the new rules generator.</p> + +<p>As a reminder, Scout2 is available on <a target="_blank" +href="https://github.com/nccgroup/Scout2">Github</a>, feedback is appreciated, +and feature requests and pull requests are welcome. The Scout2 documentation is +available at <a target="_blank" +href="https://nccgroup.github.io/Scout2">https://nccgroup.github.io/Scout2</a>.</p> + + Mon, 08 Aug 2016 23:28:00 -0700 + http://l01cd3v.github.io/aws/2016/08/08/blackhat_slides.html + http://l01cd3v.github.io/aws/2016/08/08/blackhat_slides.html + + + + Efficient review of AWS security groups' CIDR grants + <p>A significant challenge for companies using the cloud lies in ensuring that +their firewall rules follow the principle of least privilege. It is +extremely common nowadays to delegate management of security groups to developers, for +both production and test environments. This means that security groups and +their associated rules are managed by a much larger number of employees than +what used to be the case in non-cloud environments, where a unique, smaller +team was in charge of managing all firewall rules. Due to the more dynamic +nature of cloud-based infrastructures, companies should review their cloud +environment&#39;s firewall rules on a more frequent basis than for non cloud-based +systems. Unfortunately, this is a difficult exercise due to the large number of +<a href="https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing">CIDRs</a> that may be whitelisted in a given AWS account. Keeping track of all +known CIDRs and what hosts or networks they represent is not easy for +employees, and is almost impossible for external auditors who must perform the +review within a limited timeframe.</p> + +<p>In this post, I will document how this issue can be addressed using the +<a href="https://github.com/iSECPartners/AWS-recipes">AWS-Recipes</a> tools and +<a href="https://github.com/iSECPartners/Scout2">Scout2</a>.</p> + +<h3>Feed custom ip-ranges files to Scout2</h3> + +<p>Today, I am excited to announce that Scout2 accepts JSON files that +contain known CIDRs along with arbitrary metadata such as the host or network +they represent. When provided with such files, Scout2&#39;s report displays the +&quot;friendly name&quot; of each known CIDR that is whitelisted in security group rules. +This means that, instead of reviewing a list of obscure IP ranges, users of +Scout2 may now rely on the name associated with each CIDR.</p> + +<p>In order to use this new feature, Scout2 should be run with the following +arguments:</p> +<div class="highlight"><pre><code class="language-" data-lang="">./Scout2.py --profile nccgroup --ip-ranges ip-ranges-nccgroup.json ip-ranges-ncc-offices.json --ip-ranges-key-name name +</code></pre></div> +<p>In the above command line, Scout2 receives two ip-ranges JSON files via the +&quot;--ip-ranges&quot; argument:</p> + +<ul> +<li>ip-ranges-nccgroup.json, which contains the public IP addresses in the AWS IP space in use</li> +<li>ip-ranges-ncc-offices.json, which contains the public IP addresses of several offices</li> +</ul> + +<p>Furthermore, the &quot;--ip-ranges-key-name&quot; argument indicates which JSON field to +display as the &quot;friendly name&quot;.</p> + +<p>The following screenshot illustrates that, in the Scout2 report, the name of +each known CIDR is displayed. When an IP which belongs to a known CIDR is +whitelisted, the name of the corresponding CIDR is used. In this example, +5.5.5.42/32 belongs to the 5.5.5.0/24 CIDR, which is associated with the &quot;San +Francisco&quot; office. An &quot;Unknown CIDR&quot; value is displayed when an unknown value +is whitelisted.</p> + +<p><img src="/images/aws/awsscout2-ec2-cidr-friendlyname.png" alt="Screenshot: Security group rules display the name of each known CIDRs"></p> + +<p>The next section of this blog post documents how users can create and manage +these ip-ranges JSON files.</p> + +<h3>Manage known CIDRs with aws_recipes_create_ip_ranges.py</h3> + +<p>With AWS releasing their public IP address ranges, I decided to create a tool +that allows creation and management of arbitrary IP address ranges using the +same JSON format. The tool is released on GitHub at +<a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_create_ip_ranges.py">https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_create_ip_ranges.py</a> +and may be used in several scenarios:</p> + +<ul> +<li>Automatically create ip-ranges files based on public IP addresses in AWS (Elastic IPs and EC2 instances)</li> +<li>Automatically create ip-ranges files based on IP addresses documented in a CSV file</li> +<li>Manually create and manage ip-ranges files</li> +</ul> + +<p>Each of these use cases is detailed in an example below, with detailed input, +commands, and output contents.</p> + +<p><strong><em>Note:</em></strong> In the commands below, the &quot;--debug&quot; argument is used to +output pretty-printed JSON, for documentation purposes.</p> + +<h4>Automatically create ip-ranges based on public IP addresses in an AWS account</h4> + +<p>First, this tool may be used to create an ip-ranges file that contains an AWS +account&#39;s elastic IP addresses and EC2 instances&#39; public IP addresses. By doing +so, AWS users will be able to maintain a list of public IP addresses in the AWS +IP space that are associated with their resources. Assuming that AWS +credentials are configured under the &quot;nccgroup&quot; profile name, the command +below may be used:</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_create_ip_ranges.py --profile nccgroup --debug + +Fetching public IP information for the 'nccgroup' environment... +...in us-east-1: EC2 instances +...in us-east-1: Elastic IP addresses +...in ap-northeast-1: EC2 instances +...in ap-northeast-1: Elastic IP addresses +...in eu-west-1: EC2 instances +...in eu-west-1: Elastic IP addresses +...in ap-southeast-1: EC2 instances +...in ap-southeast-1: Elastic IP addresses +...in ap-southeast-2: EC2 instances +...in ap-southeast-2: Elastic IP addresses +...in us-west-2: EC2 instances +...in us-west-2: Elastic IP addresses +...in us-west-1: EC2 instances +...in us-west-1: Elastic IP addresses +...in eu-central-1: EC2 instances +...in eu-central-1: Elastic IP addresses +...in sa-east-1: EC2 instances +...in sa-east-1: Elastic IP addresses +</code></pre></div> +<p>My test environment has one elastic IP address that is not associated +with an AWS resource, and one EC2 instance that has a non-elastic public IP. +Executing the above command results in the creation of an +&quot;ip-ranges-nccgroup.json&quot; file that has the following contents:</p> +<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> + </span><span class="nt">"createDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2015-11-16-22-49-27"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"prefixes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"instance_id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"i-11223344"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"1.1.1.1"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"is_elastic"</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="p">,</span><span class="w"> + </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Test EC2 instance"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"region"</span><span class="p">:</span><span class="w"> </span><span class="s2">"us-west-2"</span><span class="w"> + </span><span class="p">},</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"instance_id"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> + </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2.2.2.2"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"is_elastic"</span><span class="p">:</span><span class="w"> </span><span class="kc">true</span><span class="p">,</span><span class="w"> + </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="kc">null</span><span class="p">,</span><span class="w"> + </span><span class="nt">"region"</span><span class="p">:</span><span class="w"> </span><span class="s2">"us-west-2"</span><span class="w"> + </span><span class="p">}</span><span class="w"> + </span><span class="p">]</span><span class="w"> +</span><span class="p">}</span><span class="w"> +</span></code></pre></div> +<h4>Automatically create ip-ranges from CSV files</h4> + +<p>From experience, I know that many companies maintain a list of their +public IP addresses, along with other network configuration information, in alternate formats, such as CSV. In +order to help with the conversion, the tool supports reading CIDR information +from CSV files. The tool was designed to be flexible and allow the creation of IP +ranges from any CSV file. In this blog post, I provide two examples.</p> + +<p>This first example demonstrates how to use the tool to build a JSON file based +on the CSV column headers. Only attributes specified on the command line will +be copied over.</p> + +<p>Contents of test1.csv:</p> +<div class="highlight"><pre><code class="language-" data-lang="">ip_prefix, discarded_value, name +4.4.4.0/24, ncc group, NY office +# This is a comment... +5.5.5.0/24, ncc group, Seattle office +</code></pre></div> +<p>Command line to convert the contents of the CSV file into JSON:</p> +<div class="highlight"><pre><code class="language-" data-lang="">./aws_recipes_create_ip_ranges.py --csv-ip-ranges test1.csv --attributes ip_prefix name --profile ncc-test1 --debug +</code></pre></div> +<p>Contents of ip-ranges-ncc-test1.json:</p> +<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> + </span><span class="nt">"createDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2015-11-17-10-22-42"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"prefixes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"4.4.4.0/24"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">" NY office"</span><span class="w"> + </span><span class="p">},</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5.5.5.0/24"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">" Seattle office"</span><span class="w"> + </span><span class="p">}</span><span class="w"> + </span><span class="p">]</span><span class="w"> +</span><span class="p">}</span><span class="w"> +</span></code></pre></div> +<p>The second example demonstrates how to use the tool to parse a CSV file with +custom column names and separate columns for the base IP and subnet mask. The +&quot;--mappings&quot; argument determines how columns will be mapped to the JSON +file&#39;s attributes.</p> + +<p>Contents of test2.csv</p> +<div class="highlight"><pre><code class="language-" data-lang="">Base IP, Dotted Subnet Mask, Subnet Mask, Something, Name, Something else +3.3.3.0, 255.255.255.0, /24, Value to discard, SF Office, Other value to discard +</code></pre></div> +<p>Command line to convert the contents of the CSV file into JSON:</p> +<div class="highlight"><pre><code class="language-" data-lang="">./aws_recipes_create_ip_ranges.py --csv-ip-ranges test2.csv --attributes ip_prefix mask name --mappings 0 2 4 --profile ncc-test2 --skip-first-line --debug +</code></pre></div> +<p>Contents of ip-ranges-ncc-test2.json</p> +<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> + </span><span class="nt">"createDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2015-11-17-10-07-22"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"prefixes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"3.3.3.0/24"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">" SF Office"</span><span class="w"> + </span><span class="p">}</span><span class="w"> + </span><span class="p">]</span><span class="w"> +</span><span class="p">}</span><span class="w"> +</span></code></pre></div> +<h4>Manually create and update ip-ranges</h4> + +<p>In case CIDRs were not managed in a CSV file, the tools offers an interactive +mode that may be leveraged to manually create a JSON ip-ranges file. The +following snippet illustrates how to use the tool to interactively create new +ip-ranges JSON files:</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name + +Add a new IP prefix to the ip ranges (y/n)? +y +Enter the new IP prefix: +5.5.5.0/24 +You entered "5.5.5.0/24". Is that correct (y/n)? +y +Enter the 'name' value: +San Francisco +You entered "San Francisco". Is that correct (y/n)? +y +Add a new IP prefix to the ip ranges (y/n)? +y +Enter the new IP prefix: +6.6.6.6/32 +You entered "6.6.6.6/32". Is that correct (y/n)? +y +Enter the 'name' value: +San Francisco +You entered "San Francisco". Is that correct (y/n)? +y +Add a new IP prefix to the ip ranges (y/n)? +n +</code></pre></div> +<p>Contents of ip-ranges-ncc-offices.json:</p> +<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> + </span><span class="nt">"createDate"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2015-11-16-22-44-38"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"prefixes"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5.5.5.0/24"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"San Francisco"</span><span class="w"> + </span><span class="p">},</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"ip_prefix"</span><span class="p">:</span><span class="w"> </span><span class="s2">"6.6.6.6/32"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"San Francisco"</span><span class="w"> + </span><span class="p">}</span><span class="w"> + </span><span class="p">]</span><span class="w"> +</span><span class="p">}</span><span class="w"> +</span></code></pre></div> +<p>The tool can also automatically add new CIDRs to existing ip-ranges files:</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name --debug + +Loading existing IP ranges from ip-ranges-ncc-offices.json +Add a new IP prefix to the ip ranges (y/n)? +y +Enter the new IP prefix: +7.7.7.7/32 +You entered "7.7.7.7/32". Is that correct (y/n)? +y +Enter the 'name' value: +Seattle +You entered "Seattle". Is that correct (y/n)? +y +Add a new IP prefix to the ip ranges (y/n)? +n +File 'ip-ranges-ncc-offices.json' already exists. Do you want to overwrite it (y/n)? +y + +$ cat ip-ranges-ncc-offices.json +{ + "createDate": "2015-11-16-22-44-38", + "prefixes": [ + { + "ip_prefix": "5.5.5.0/24", + "name": "San Francisco" + }, + { + "ip_prefix": "6.6.6.6/32", + "name": "San Francisco" + }, + { + "ip_prefix": "7.7.7.7/32", + "name": "Seattle" + } + ] +} +</code></pre></div> +<h3>Conclusion</h3> + +<p>This addition to Scout2 provides AWS account administrators and auditors with +an improved insight into their environment. Usage of this feature should result +in further hardened security groups because detection of unknown whitelisted +CIDRs and understanding of existing rules is significantly easier.</p> + +<p>I am currently working on a major rework of Scout2&#39;s reporting engine, +which will further improve reporting and allow creation of new alerts when an +unknown CIDR is whitelisted.</p> + + Tue, 17 Nov 2015 08:00:00 -0800 + http://l01cd3v.github.io/aws/2015/11/17/ip_ranges_management.html + http://l01cd3v.github.io/aws/2015/11/17/ip_ranges_management.html + + + + Redshift support added in Scout2 + <p>Today, I am excited to announce that support for Redshift was added in +Scout2. By default, Scout2 will fetch information about your Redshift clusters, +cluster parameter groups, and cluster security groups if you still use +EC2-Classic. At this stage, Scout2 comes with six Redshift security rules that +are enabled by default:</p> + +<ul> +<li>Clusters + +<ul> +<li>Check whether version upgrade is enabled</li> +<li>Check whether the cluster is publicly accessible</li> +<li>Check whether database encryption is enabled</li> +</ul></li> +<li>Cluster parameter groups + +<ul> +<li>Check whether SSL/TLS is required to access the database</li> +<li>Check whether user activity logging is enabled</li> +</ul></li> +<li>Cluster security groups (EC2-classic) + +<ul> +<li>Check whether the security group allows access to all IP addresses (0.0.0.0/0)</li> +</ul></li> +</ul> + +<p>Scout2 was first released over a year and a half ago, and proved to be extremely +helpful when performing AWS configuration reviews. While +Scout2&#39;s initial release only supported three services (IAM, EC2, and S3) and +included thirteen security checks, the tool rapidly grew to add support for RDS +and CloudTrail. Furthermore, the tool now offers over fifty tests throughout +these five supported services. I hope that support for Redshift will bring +value to users of Scout2, and welcome feature requests, bug reports, and +recommendations on Github at +<a href="https://github.com/iSECPartners/Scout2/issues">https://github.com/iSECPartners/Scout2/issues</a>.</p> + + Thu, 06 Aug 2015 08:39:00 -0700 + http://l01cd3v.github.io/aws/2015/08/06/scout2_supports_redshift.html + http://l01cd3v.github.io/aws/2015/08/06/scout2_supports_redshift.html + + + + Introducing opinel: Scout2's favorite tool + <p>With boto3 being stable and generally available<sup><a href="https://aws.amazon.com/about-aws/whats-new/2015/06/boto3-aws-sdk-for-python-version-3-is-now-generally-available">1</a></sup>, I took the opportunity +to migrate Scout2 and AWS-recipes to boto3. As part of that migration +effort, I decided to publish the formerly-known-as AWSUtils repository -- used +by Scout2 and AWS-recipes -- as a python package required by these tools, +rather than requiring users to work with Git submodules. I&#39;ve also added more +flexibility when working with MFA-protected API calls and improved versioning +across the project.</p> + +<h3>opinel</h3> + +<p>To avoid name conflicts, I decided to rename the shared AWSUtils code to a +less misleading name: opinel. The opinel package is published on <a href="https://pypi.python.org/pypi/opinel">PyPI</a>, and thus can +be installed using pip and easy_install. The corresponding source code is still +open-sourced on Github at <a href="https://github.com/iSECPartners/opinel">https://github.com/iSECPartners/opinel</a>. +As a result, Scout2 and AWS-recipes have been modified to list opinel as a +requirement, which significantly simplifies installation and management of this +shared code.</p> + +<h3>Support for Python 2.7 and 3.x</h3> + +<p>Because boto3 supports both Python2 and Python3, I decided to make sure that +the code built on top of that package has similar properties. As a result, +the latest versions of Scout2 and AWS-recipes support Python 2.7 and 3.x. +Note that opinel will <strong>NOT</strong> work with Python 2.6.</p> + +<h3>Modification of the MFA workflow</h3> + +<p>As requested by a user of AWS-recipes<sup><a href="https://github.com/iSECPartners/opinel/issues/4">2</a></sup>, I modified the workflow when +using MFA-protected API access to no longer store the long-lived credentials +in a separate file. As a result, the <em>.aws/credentials.no-mfa</em> file is no +longer supported and all credentials are stored in the standard AWS credentials +file under <em>.aws/credentials</em>. Usage of the existing tools remains unchanged, +but the long-lived credentials are now accessible via a new profile name: +<em>profile_name-nomfa</em>. This allows users to work with both STS and long-lived +credentials if need be.</p> + +<p>If you already had configured your environment to work with MFA-protected API +access, you will need to copy your long-lived credentials back to the +<em>.aws/credentials</em> file. This can be done with a simple command such as the +following:</p> +<div class="highlight"><pre><code class="language-" data-lang="">cat ~/.aws/credentials.no-mfa | sed -e 's/]$/-nomfa]/g' &gt;&gt; ~/.aws/credentials +</code></pre></div> +<h3>Support to use assumed-role credentials</h3> + +<p>With this new workflow implemented, I created a new recipe that allows +configuration of role-credentials in the <em>.aws/credentials</em> file. When the following +command is run, it uses the credentials associated with the <em>isecpartners</em> +profile to request role credentials for the IAM-Scout2 role. The role +credentials are then written in the <em>.aws/credentials</em> file in a new profile +named <em>isecpartners-Scout2</em>, which is the profile name appended by the role +session name.</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_assume_role.py --profile isecpartners --role-arn arn:aws:iam::AWS_ACCOUNT_ID:role/IAM-Scout2 --role-session-name Scout2 +</code></pre></div> +<p>Users can then use their favorite tools that support profiles. For example, +Scout2 could be run with the following command line:</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ./Scout2.py --profile isecpartners-Scout2 +</code></pre></div> +<p>Note that this recipe supports MFA if the assumed role requires it:</p> + +<ul> +<li>If you never configured your environment to work with MFA, you can provide your MFA serial number (ARN) and current token code as arguments.</li> +<li>If you already configured your environment to work with MFA and stored your MFA serial in the <em>.aws/credentials</em> file, you just need to pass your token code as an additional argument.</li> +<li>Finally, if you already initiated an STS session, you do not need to provide a new token code and can run the command as above.</li> +</ul> + +<h3>Conclusion</h3> + +<p>With the release of opinel, I hope to simplify distribution and management of +the code shared between Scout2 and AWS-recipes. Additionally, I +significantly modified the workflow and credentials storage when working with +MFA-protected API calls, which allows users to use both their long-lived and STS +credentials.</p> + + Mon, 03 Aug 2015 11:08:00 -0700 + http://l01cd3v.github.io/aws/2015/08/03/boto3_opinel_scout_recipes.html + http://l01cd3v.github.io/aws/2015/08/03/boto3_opinel_scout_recipes.html + + + + IAM user management strategy (part 2) + <p>The previous <a href="/aws/2015/02/24/iam_user_management.html">IAM user management strategy</a> post discussed how +usage of IAM groups enables AWS administrators to consistently grant privileges +and enforce a number of security rules (such as MFA-protected API access). This +blog post will build on this idea by introducing category groups and +documenting new tools to improve IAM user management.</p> + +<h3>Categorize your IAM users</h3> + +<p>For a variety of reasons, applying a single set of security rules to all IAM +users is not always practical. For example, because many applications running +in AWS predate IAM roles, numerous environments still rely on the existence of +headless IAM users. Additionally, third parties may be granted access to an AWS +account for a number of reasons but may not be able to comply with the same set +of security rules that employees follow. For this reason, NCC recommends using +category groups to sort IAM users and reliably enforce appropriate security +measures. For example, one group for all human users and a second for all headless users may be +created: MFA-protected API access and password management are not relevant for +headless users. Furthermore, human users may be categorized into several groups +such as employees and contractors: API access can be restricted to the +corporate IP range for employees but might not be achievable for contractors.</p> + +<p><em>Note 1:</em> The set of category groups should define all types of IAM users that +may exist in your AWS account and each IAM user should belong to one -- and +only one -- category group (they may belong to other groups though).</p> + +<p><em>Note 2:</em> The common group and category groups should be used to enable enforcing +security in one&#39;s AWS environment. Policies attached to these groups should be +carefully reviewed and grant the minimum set of privileges necessary for this +type of IAM user (<em>e.g.</em> credential management for humans).</p> + +<h3>Example of category groups</h3> + +<p>The rest of this article describes a number of tools developed and used by +NCC to help implement this IAM user management strategy. These tools can be found +in the <a href="https://github.com/iSECPartners/AWS-recipes">AWS-Recipes</a> repository. We will +use our test AWS environment as an example, in which we use three category groups in +addition to the <em>AllUsers</em> common group:</p> + +<ol> +<li><em>AllHumans</em>, the group all employees must belong to.</li> +<li><em>AllHeadlessUsers</em>, the group all headless IAM users must belong to.</li> +<li><em>AllMisconfiguredUsers</em>, a placeholder for sample misconfigured users.</li> +</ol> + +<p>We also have an IAM user naming convention that requires usernames to match the +following schema:</p> + +<ol> +<li>Employees: firstname initial appended with lastname</li> +<li>Headless user: name of the service prefixed with <em>HeadlessUser-</em></li> +<li>Misconfigured: description of the misconfiguration prefixed with <em>MisconfiguredUser-</em></li> +</ol> + +<p>Based on these rules, we created a configuration file stored under +<em>.aws/recipes/isecpartners.json</em>, with <em>isecpartners</em> matching the profile&#39;s +name. If you do not use profiles, the configuration will be under +<em>.aws/recipes/default.json</em>.</p> +<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> + </span><span class="nt">"common_groups"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"AllUsers"</span><span class="w"> </span><span class="p">],</span><span class="w"> + </span><span class="nt">"category_groups"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="s2">"AllHumanUsers"</span><span class="p">,</span><span class="w"> + </span><span class="s2">"AllHeadlessUsers"</span><span class="p">,</span><span class="w"> + </span><span class="s2">"AllMisconfiguredUsers"</span><span class="w"> + </span><span class="p">],</span><span class="w"> + </span><span class="nt">"category_regex"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="s2">""</span><span class="p">,</span><span class="w"> + </span><span class="s2">"^Headless-(.*)"</span><span class="p">,</span><span class="w"> + </span><span class="s2">"^MisconfiguredUser-(.*)"</span><span class="w"> + </span><span class="p">],</span><span class="w"> + </span><span class="nt">"profile_name"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"isecpartners"</span><span class="w"> </span><span class="p">]</span><span class="w"> +</span><span class="p">}</span><span class="w"> +</span></code></pre></div> +<p>This configuration file declares the name of the common IAM group and two lists +related to the categorization of IAM users:</p> + +<ol> +<li>A list of category groups.</li> +<li>A list of regular expressions matching our naming convention.</li> +</ol> + +<p><em>Note 1:</em> If you do not have a naming convention in place to distinguish the +type of user, remove the <em>category_regex</em> attribute from your configuration +file.</p> + +<p><em>Note 2:</em> If a regular expression is only applicable to a subset of category +groups, you must ensure that both lists have the same length and use an empty +string for groups that cannot be automatically associated (see the +<em>AllHumanUsers</em> group in our example).</p> + +<p><em>Note 3:</em> Use of a configuration file is not necessary as all values may be +passed as command line arguments. If a configuration file exists and a value is +passed as an argument, the value passed via the command line will be used.</p> + +<h3>Create your default groups with <em><a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_iam_create_default_groups.py">aws_iam_create_default_groups.py</a></em></h3> + +<p>The purpose of this tool is to create IAM groups whose name matches the common +and category groups specified in the above configuration file. Running the +following command results in four new groups being created if they did not +already exist.</p> +<div class="highlight"><pre><code class="language-" data-lang="">./aws_iam_create_default_groups.py --profile isecpartners +</code></pre></div> +<h3>(Automatically) sort IAM users with <em><a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_iam_sort_users.py">aws_iam_sort_users.py</a>.</em></h3> + +<p>This tool iterates through all IAM users and attempts to automatically detect +the IAM groups each user should belong to. For convenience, we recommend adding +the following to your AWS recipes configuration files:</p> +<div class="highlight"><pre><code class="language-" data-lang="">"aws_sort_users.py": { + "create_groups": false, +}, +"force_common_group": true +</code></pre></div> +<p>This specifies default values for additional arguments to be set when running +<em>aws_iam_sort_users.py</em>. Specifically, with these values, running this tool +will automatically add all IAM users to the common group <em>AllUsers</em> and will +not attempt to create the default groups (not necessary as we already did +this). Additionally, this tool checks that each IAM user belongs to one of the +category groups. If this is not the case and the username matches a regular +expression, the user is automatically added to the matching category group. Otherwise, a +multi-choice prompt appears to allow manual selection of the appropriate +category group.</p> + +<h3>Additional advantages of configuration files</h3> + +<p>Besides helping with simplification of these tools&#39; usage, this new AWS-recipe +configuration file can be used across tools, allowing for more consistent +rule enforcement. For example, the +<em><a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_iam_create_user.py">aws_iam_create_user.py</a>.</em> +tool uses this configuration file and applies the same business logic to add +users to the common group and appropriate category group at user creation time. In +our test environment, for example, running the following command automatically +added the new user to the <em>MisconfiguredUser</em> group:</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_iam_create_user.py --profile isecpartners --users MisconfiguredUser-BlogPostExample +Creating user MisconfiguredUser-BlogPostExample... +Save unencrypted value (y/n)? y +User 'MisconfiguredUser-BlogPostExample' does not belong to the mandatory common group 'AllUsers'. Do you want to remediate this now (y/n)? y +User 'MisconfiguredUser-BlogPostExample' does not belong to any of the category group (AllHumanUsers, AllHeadlessUsers, AllMisconfiguredUsers). Automatically adding... +Enabling MFA for user MisconfiguredUser-BlogPostExample... +</code></pre></div> +<h3>Conclusion</h3> + +<p>While efficient and reliable management of IAM users can be challenging, using +the right strategy and tools significantly simplifies this process. Creation +and use of a naming convention for IAM users enables +automated user management and enforcement of security rules.</p> + + Tue, 09 Jun 2015 09:20:00 -0700 + http://l01cd3v.github.io/aws/2015/06/09/iam_user_management_2.html + http://l01cd3v.github.io/aws/2015/06/09/iam_user_management_2.html + + + + Work daily with enforced MFA-protected API access + <h3>AWS Security Token Service</h3> + +<p>The AWS Security Token Service (STS) is the gateway used to create sessions +when MFA-protected API access is enabled. This service allows IAM users to +retrieve short-lived credentials (<em>i.e</em> access key ID, secret access key, and +session token) in exchange for their long-lived credentials (<em>i.e.</em> AWS access +key ID and secret key) and their current authentication code. When enforcing +MFA-protected API access, as recommended in the previous <a href="/aws/2015/04/02/use_and_enforce_mfa.html">Use and enforce +Multi-Factor +Authentication</a> post, +IAM users must use these short-lived credentials to access other AWS services.</p> + +<h3>Challenges with MFA-protected API access</h3> + +<p>When MFA-protected API access is enforced, managing AWS access keys becomes +challenging because configuration files that contain these credentials must be +updated regularly. Users must also ensure that they do not lose their +long-lived credentials when modifying the configuration files to write their +short-lived credentials. In order to help with this workflow, iSEC wrote and +released several simple tools in the +<a href="https://github.com/iSECPartners/AWS-recipes">AWS-recipes</a> repository.</p> + +<p>The collection of tools that we will discussed below uses the &quot;<a +target="_blank" +href="https://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs">new +and standardized way to manage credentials in the AWS SDKs</a>&quot;, meaning that +SDKs are expecting to read credentials from the <em>.aws/credentials</em> file under +the user&#39;s home or profile directory.</p> + +<h3>aws_recipes_configure_iam.py</h3> + +<p>The +<a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_configure_iam.py">aws_recipes_configure_iam.py</a> +tool allows users to configure and store their long-lived credentials in a new, +non-standard, <em>.aws/credentials.no-mfa</em> file. In addition to prompting for the +AWS access key ID and secret key, this tool also prompts for the MFA device +serial number because this information must be provided when making calls to +the STS API. Similar to the AWS CLI and SDKs, it supports profile names. The +following code snippet is an example of calling this tool to configure a new +profile called <em>isecpartners</em>:</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_configure_iam.py --profile isecpartners +AWS Access Key ID: AWS_KEY_ID +AWS Secret Access Key: AWS_SECRET_KEY +AWS MFA serial: arn:aws:iam::AWS_ACCOUNT_ID:mfa/USER_NAME +</code></pre></div> +<p>When looking at the <em>.aws</em> folder, we can see that a <em>credentials.no-mfa</em> file +exists and that it contains the credentials that were just entered:</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ls -l ~/.aws +total 4 +-rw-r--r-- 1 loic loic 93 Apr 3 14:00 credentials.no-mfa +$ cat ~/.aws/credentials.no-mfa +[isecpartners] +aws_access_key_id = AWS_KEY_ID +aws_secret_access_key = AWS_SECRET_KEY +aws_mfa_serial = arn:aws:iam::AWS_ACCOUNT_ID:mfa/USER_NAME +</code></pre></div> +<p>Now that long-lived credentials are configured, we can use the next tool to +call the AWS STS API and request short-lived credentials that will be used +to access other AWS services.</p> + +<h3>aws_recipes_init_sts_session.py</h3> + +<p>The +<a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_init_sts_session.py">aws_recipes_init_sts_session.py</a> +tool reads long-lived credentials configured in the .aws/credentials.no-mfa +file, prompts users for their MFA code, and retrieves STS credentials (AWS +access key ID, AWS secret key, and session token). The short-lived credentials +are then saved under the standardized <em>.aws/credentials</em> file to be accessible +to the AWS CLI and other tools built with the AWS SDKs. The following code +snippet demonstrates calling this tool to request an STS session token:</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ./aws_recipes_init_sts_session.py --profile isecpartners +Enter your MFA code: 123456 +Successfully configured the session token for profile 'isecpartners'. +</code></pre></div> +<p>When looking at the <em>.aws</em> folder, we can see that a standard <em>credentials</em> +file now exists as well and that it contains the short-lived credentials:</p> +<div class="highlight"><pre><code class="language-" data-lang="">$ ls -l ~/.aws +total 8 +-rw-r--r-- 1 loic loic 576 Apr 3 14:14 credentials +-rw-r--r-- 1 loic loic 179 Apr 3 14:00 credentials.no-mfa +$ cat ~/.aws/credentials +[isecpartners] +aws_access_key_id = STS_KEY_ID +aws_secret_access_key = STS_SECRET_KEY +aws_mfa_serial = arn:aws:iam::AWS_ACCOUNT_ID:mfa/USER_NAME +aws_session_token = AWS//////////SESSION_TOKEN +</code></pre></div> +<p>Now that the short-lived credentials are configured, we can use the AWS CLI or +other tools built with the AWS SDKs that read credentials from this standard +location. When the STS session expires, users just need to re-run the +<a href="https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_init_sts_session.py">aws_recipes_init_sts_session.py</a> +tool and the standard <em>credentials</em> file will be updated with new valid +short-lived credentials.</p> + +<h3>Conclusion</h3> + +<p>By using this pair of tools to manage their AWS access keys, IAM users can +easily use the AWS CLI and other tools built with various AWS SDKs in +environments that have been secured and enforce MFA-protected API access.</p> + + Fri, 03 Apr 2015 14:10:00 -0700 + http://l01cd3v.github.io/aws/2015/04/03/session_management_tools.html + http://l01cd3v.github.io/aws/2015/04/03/session_management_tools.html + + + + Use and enforce Multi-Factor Authentication + <h3>What is Multi-Factor Authentication?</h3> + +<p>When enabled, Multi-Factor Authentication (MFA) provides strong +defense-in-depth against compromises of credentials. MFA-enabled users have a +device that periodically generates a new authentication code (<em>i.e.</em> one-time +password); they need to enter the current authentication code along with their +static credentials (<em>i.e.</em> username and password) in order to successfully +authenticate. In addition to supporting MFA when accessing the web console +(<em>i.e.</em> password-based authentication), AWS also offers MFA-protected API +access for users who work with AWS access keys. Through the Security Token +Service (STS), IAM users can request temporary credentials in exchange for +their long-lived credentials (<em>i.e.</em> AWS access key ID and secret key) and +their current authentication code.</p> + +<h3>Why should one use and enforce MFA?</h3> + +<p>For companies deploying their application in the cloud, a breach that results +in unauthorized access to the management console &mdash; or API &mdash; is the +worst-case scenario. While a number of AWS administrators have realized the +importance of enabling MFA when they access the web console, a limited number +of them enforce MFA-protected API access. This represents a huge gap in one&#39;s +security posture because AWS access keys do not come with as many security +features as passwords do:</p> + +<ul> +<li>AWS administrators can enforce password expiration; this is currently not +possible for AWS access keys.</li> +<li>While it is probably safe to assume that most AWS administrators do not store +their password in plaintext, most of them use AWS access keys. By design, these +keys are meant to be stored in plaintext files that are accessed by tools built +with the various AWS SDKs.</li> +<li>A lost password is a forgotten password; a lost key is a key stored in a lost +file, which may be on an unencrypted storage device (e.g. hard drive or USB Flash +drive).</li> +</ul> + +<p>Because AWS access keys are long-lived credentials that are stored in plaintext +files, they are more susceptible to compromise than passwords. It is therefore +necessary to enable MFA when the AWS API is accessed using these keys and not +only when users sign in using their passwords.</p> + +<h3>How can one enforce MFA?</h3> + +<p>Unfortunately, at time of writing, AWS does not offer an option to enforce +MFA-protected API access via a global setting. Therefore, AWS account +administrators must carefully manage their IAM users and develop a strategy to +reliably achieve this. In order to enforce MFA-protected API access, iSEC +recommends the following:</p> + +<ol> +<li>Create a common IAM group that all IAM users belong to, as discussed in the previous <a href="/aws/2015/02/24/iam_user_management.html">IAM user management strategy</a> post.</li> +<li>Add the following policy (also available on <a href="https://github.com/iSECPartners/AWS-recipes/blob/master/IAM-Policies/EnforceMFA-8HourSession.json">Github</a>) to enforce MFA for all users who belong to this group.</li> +</ol> + +<p>This policy will enforce MFA regardless of how the IAM user authenticated with +AWS; it will be effective whether they use password-based or key-based +authentication.</p> +<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> + </span><span class="nt">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2012-10-17"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Statement"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Deny"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Condition"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> + </span><span class="nt">"Null"</span><span class="p">:{</span><span class="nt">"aws:MultiFactorAuthAge"</span><span class="p">:</span><span class="s2">"true"</span><span class="p">}</span><span class="w"> + </span><span class="p">}</span><span class="w"> + </span><span class="p">},</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Deny"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Condition"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w"> + </span><span class="nt">"NumericGreaterThan"</span><span class="p">:{</span><span class="nt">"aws:MultiFactorAuthAge"</span><span class="p">:</span><span class="s2">"28800"</span><span class="p">}</span><span class="w"> + </span><span class="p">}</span><span class="w"> + </span><span class="p">}</span><span class="w"> + </span><span class="p">]</span><span class="w"> +</span><span class="p">}</span><span class="w"> +</span></code></pre></div> +<p>The first statement in the above policy denies all actions if the +<em>aws:MultiFactorAuthAge</em> key is not present; this key only exists if MFA is +used [1].</p> + +<p>The second statement verifies that the validation of the MFA code was performed +less than eight hours ago. Temporary credentials may be valid for a duration +between fifteen minutes and thirty-six hours [2]. iSEC recommends requiring +users to initiate a new session at least once a day.</p> + +<p><strong><em>Note:</em></strong> An &quot;explicit deny&quot; means that, regardless of other policies granted +to a user, this deny rule will prevail. More information about the IAM policy +evaluation logic can be found in the AWS documentation at <a +target="_blank" +href="http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_EvaluationLogic.html">http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_EvaluationLogic.html</a>.</p> + +<h3>Use AWS Scout2 to detect users without MFA</h3> + +<p>The default ruleset used by <a href="https://github.com/iSECPartners/Scout2">AWS Scout2</a> includes a rule that checks for IAM users who have password-based authentication enabled but do not have an MFA device configured. If Scout2 detects IAM users with password-based authentication enabled and no MFA device, it will document a &quot;Lack of MFA&quot; security risk in the IAM menu dropdown, as illustrated in the below screenshot.</p> + +<p><img src="/images/aws/awsscout2-user-nomfa-1.png" alt="Screenshot: IAM menu dropdown with a &quot;Lack of MFA&quot; security risk"></p> + +<p>When clicked, this &quot;Lack of MFA&quot; link filters the list of IAM users to display +those who have password-based authentication enabled but no MFA device +configured. The red &quot;No&quot; following &quot;Multi-Factor enabled&quot; indicates a danger +tied to that particular IAM user.</p> + +<p><img src="/images/aws/awsscout2-user-nomfa-2.png" alt="Screenshot: Red &quot;No&quot; indicating that this IAM user may access the web console without MFA"></p> + +<h3>How can one use MFA with command line tools?</h3> + +<p>Users of the AWS CLI (and other command line tools) have several methods to +configure their credentials, such as environment variables, configuration +files, or command line arguments. However, updating these settings on a daily +basis when MFA-protected API access is enabled is inconvenient. To help +facilitate this work flow, iSEC has created a set of Python tools and released +them in the <a href="https://github.com/iSECPartners/AWS-recipes">AWS-recipes</a> +repository. Further details about these tools will be published in the next +blog post.</p> + +<p>Additional information about MFA with AWS is available in the AWS +documentation at <a target="_blank" +href="https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_ManagingMFA.html"> +https://docs.aws.amazon.com/IAM/latest/UserGuide/Using_ManagingMFA.html</a>.</p> + +<h3>Conclusion</h3> + +<p>Enforcing Multi-Factor Authentication for all IAM users is extremely important +in order to mitigate the risks of credentials compromise (especially the AWS +access key ID and secret). This aspect of security is commonly overlooked and +may result in catastrophic damages. By using a strict strategy for management +of IAM users and the above IAM policy, AWS administrators may significantly +reduce risks of account compromise.</p> + +<p>[1] <a target="_blank" href="http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#AvailableKeys">http://docs.aws.amazon.com/IAM/latest/UserGuide/AccessPolicyLanguage_ElementDescriptions.html#AvailableKeys</a></p> + +<p>[2] <a target="_blank" href="http://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html">http://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html</a></p> + + Thu, 02 Apr 2015 14:10:00 -0700 + http://l01cd3v.github.io/aws/2015/04/02/use_and_enforce_mfa.html + http://l01cd3v.github.io/aws/2015/04/02/use_and_enforce_mfa.html + + + + IAM user management strategy + <h3>Use IAM groups</h3> + +<p>When granting privileges to IAM users, AWS account administrators should avoid +use of user-specific policies. Instead, create groups whose name explicitly +defines the members&#39; job functions or responsibilities (<em>e.g.</em> AWS +Administrators, Operations, Developers, Accountants), and define the +permissions granted within group policies. Doing so will simplify the +permissions management process as changes in group policies apply to all +members.</p> + +<p>When performing AWS configuration reviews, iSEC often discovers IAM users +whose privileges have been granted via a combination of IAM user and IAM group +policies. It is not uncommon to see IAM users who are granted full +administrator privileges in a redundant manner, via both user and group +policies. Such configuration creates an avenue for configuration mistakes, as +another administrator may believe that terminating an IAM user&#39;s membership to +the admin group is sufficient. Therefore, banning use of IAM user policies +will result in making one&#39;s AWS environment less error-prone.</p> + +<p><strong><em>Note</em></strong>: It is on purpose that iSEC recommends using IAM group names that +reflect a job title or responsibility. IAM users who do not fit in such groups +(<em>e.g.</em> headless users) should not exist. Instead, AWS account administrators +should investigate use of IAM roles for EC2. Further details will be discussed +in an upcoming blog post.</p> + +<h3>Create a common IAM group to apply generic policies</h3> + +<p>Because a number of policies must be applied to all users, iSEC recommends that +AWS account administrators create an IAM group that all IAM users belong to. +Doing so will allow AWS account administrators to consistently grant privileges +and enforce a number of rules.</p> + +<p><strong><em>Note</em></strong>: It is important that all IAM users belong to this common IAM group +to ensure that policies are consistently applied. Failure to do so will create +gaps in one&#39;s AWS environment security posture.</p> + +<h3>Authorize IAM users to manage their credentials</h3> + +<p>To begin with, iSEC recommends that AWS account administrators allow all of +their IAM users to manage their credentials, and only theirs. With all IAM +users belonging to the common IAM group, this can be achieved by applying the +following IAM policy to the group.</p> +<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> + </span><span class="nt">"Version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2012-10-17"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Statement"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="s2">"iam:*AccessKey*"</span><span class="p">,</span><span class="w"> + </span><span class="s2">"iam:*Password"</span><span class="p">,</span><span class="w"> + </span><span class="s2">"iam:*MFADevice*"</span><span class="p">,</span><span class="w"> + </span><span class="s2">"iam:UpdateLoginProfile"</span><span class="w"> + </span><span class="p">],</span><span class="w"> + </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"arn:aws:iam::AWS_ACCOUNT_ID:user/${aws:username}"</span><span class="w"> + </span><span class="p">},</span><span class="w"> + </span><span class="p">{</span><span class="w"> + </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> + </span><span class="s2">"iam:CreateVirtualMFADevice"</span><span class="p">,</span><span class="w"> + </span><span class="s2">"iam:DeleteVirtualMFADevice"</span><span class="w"> + </span><span class="p">],</span><span class="w"> + </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"arn:aws:iam::AWS_ACCOUNT_ID:mfa/${aws:username}"</span><span class="w"> + </span><span class="p">}</span><span class="w"> + </span><span class="p">]</span><span class="w"> +</span><span class="p">}</span><span class="w"> +</span></code></pre></div> +<p>While the above policy is sufficient to allow users to manage their +credentials, AWS administrators may consider the following statement as an +addition; it allows IAM users to know what the account&#39;s password policy is.</p> +<div class="highlight"><pre><code class="language-" data-lang=""><span class="p">{</span><span class="w"> + </span><span class="nt">"Effect"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Allow"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Action"</span><span class="p">:</span><span class="w"> </span><span class="s2">"iam:GetAccountPasswordPolicy"</span><span class="p">,</span><span class="w"> + </span><span class="nt">"Resource"</span><span class="p">:</span><span class="w"> </span><span class="s2">"*"</span><span class="w"> +</span><span class="p">}</span><span class="w"> +</span></code></pre></div> +<h3>Use AWS Scout2 to detect user policies</h3> + +<p>The default ruleset used by <a href="https://github.com/iSECPartners/Scout2">AWS +Scout2</a> includes a rule that checks for +user policies and reports the use of user-specific IAM policies as a warning. +Detection of user-specific IAM policies results in the IAM menu dropdown +containing a &quot;User policies&quot; security risk, as illustrated in the below screenshot.</p> + +<p><img src="/images/aws/awsscout2-iam-user-policy-1.png" alt="Screenshot: IAM menu dropdown with a User policies security risk"></p> + +<p>When clicked-on, this &quot;User policies&quot; link filters the list of IAM users to only +display those who have at least one user policy attached. The orange badge +indicates a warning and the count of user policies attached to this particular +IAM user.</p> + +<p><img src="/images/aws/awsscout2-iam-user-policy-2.png" alt="Screenshot: Orange badge indicating that at least one user policy is attached to that IAM user"></p> + +<h3>Check that all IAM users belong to the common group</h3> + +<p>AWS Scout2 comes with a tool — RulesGenerator.py — that allows AWS account +administrators to generate a custom ruleset to tailor the report to their +needs. An optional IAM rule requires all IAM users to belong to a common IAM +group. In order to enable this rule, the following can be done:</p> + +<ol> +<li>Run the rules generator with the following command line: +<pre style="margin-left: -20px; margin-top: 10px; margin-bottom: 10px"><code>./RulesGenerator.py --ruleset_name isec --services iam</pre></code></li> +<li>Answer &quot;yes&quot; to the question &quot;Would you like to ensure that all IAM users belong to a given IAM group?&quot;</li> +<li>Enter the name of your common group (<em>e.g.</em> AllUsers)</li> +<li>Enter &quot;yes&quot; or &quot;y&quot; to confirm</li> +<li>Change the level if desired</li> +<li>Run Scout2</li> +</ol> + +<p><strong><em>Note</em></strong>: If you have already run Scout2 and do not wish to download the latest +IAM configuration, use the following command to run an offline analysis:</p> +<div class="highlight"><pre><code class="language-" data-lang="">./Scout2.py --ruleset_name isec --services iam --local +</code></pre></div> +<p>The following screenshot illustrates the IAM menu dropdown containing a +security risk when IAM users do not belong to the configured common group.</p> + +<p><img src="/images/aws/awsscout2-iam-user-commongroup-1.png" alt="Screenshot: IAM menu dropdown when IAM users do not belong to the common group"></p> + +<p>When clicked-on, this link filters the list of IAM users to only display those +who do not belong to the common IAM group. A colored warning sign appears, +warning about this issue.</p> + +<p><img src="/images/aws/awsscout2-iam-user-commongroup-2.png" alt="Screenshot: Orange badge indicating that at least one user policy is attached to that IAM user"></p> + +<h3>Conclusion</h3> + +<p>Strict management of IAM users and tight control of their privileges is key in +maintaining a secure AWS environment. When followed, the above recommendations +should enable AWS administrators to manage IAM users with improved efficiency +and lower the chances of overly privileged users to exist.</p> + + Tue, 24 Feb 2015 20:49:00 -0800 + http://l01cd3v.github.io/aws/2015/02/24/iam_user_management.html + http://l01cd3v.github.io/aws/2015/02/24/iam_user_management.html + + + + Do not use your AWS root account + <h3>What is the AWS root account?</h3> + +<p>The AWS root account is the account that was used -- or created -- when signing +up with Amazon Web Services. This account has full access to all resources in +the account and it is not possible to alter this configuration.</p> + +<h3>Risks of using the AWS root account</h3> + +<p>Using the AWS root account means that there is potential for its compromise. +In particular, iSEC noticed that AWS customers who use the AWS root account +tend to do the following:</p> + +<ol> +<li>Share credentials between employees.</li> +<li>Disable Multi-Factor Authentication (MFA) for convenience.</li> +</ol> + +<p>Shared credentials, aside from increasing the risk of compromise during the +sharing process, render credential rotation impractical due to the need for the +newly-generated secret to be known by multiple parties. Sharing the AWS root +account also undermines any effort towards using IAM and leveraging the +fine-grained access controls it offers. Finally, shared credentials result in +loss of the attribution ability, which makes auditing harder and may prevent +successful investigation.</p> + +<h3>AWS Identity and Access Management (IAM)</h3> + +<p>AWS IAM allows account administrators to create users for every employee and +grant them access to a limited set of services, actions, and resources. This +allows AWS account administrators to apply the principle of least privilege, +which dictates that a given user should only be able to access the information +and resources that are necessary for them to perform tasks they are responsible +for. Additionally, use of IAM allows AWS users to rotate credentials and revoke +privileges without impacting other employees.</p> + +<p>AWS account administrators should create an <em>Administrator</em> IAM group, grant +administrator privileges to this group, and create individual IAM users for +each employee in charge of administrating the AWS account. When done, the AWS +root password should be rotated and stored in a safe manner. Furthermore, +additional credentials such as access keys and certificates should be deleted.</p> + +<h3>Important security consideration about the root account</h3> + +<p>AWS users should always enable MFA on their root account, even when the +password is securely stored; it is important to realize that the password reset +for the root account process only requires access to the email address +associated with this account. <strong>This means that, without MFA, your production +environment is only as secure as an email.</strong></p> + + Mon, 23 Feb 2015 08:42:00 -0800 + http://l01cd3v.github.io/aws/2015/02/23/do_not_use_your_root_account.html + http://l01cd3v.github.io/aws/2015/02/23/do_not_use_your_root_account.html + + + + Announcing the AWS blog post series + <p>Starting this month, iSEC Partners will start a series of blog posts related to +AWS. The goal of these blog posts will be to:</p> + +<ul> +<li>Discuss common security gaps in AWS environments</li> +<li>Discuss common security gaps in the architecture of applications deployed in +the cloud</li> +<li>Describe methods and tools used to identify these security gaps</li> +<li>Share tools and scripts that facilitate daily and secure work with AWS</li> +<li>Share AWS policies that help improve the security posture of AWS environments</li> +</ul> + +<p>To share material, iSEC created a new public +<a target="_blank" +href="https://github.com/iSECPartners/AWS-recipes">AWS-recipes</a> repository +on Github. The tools and policies shared in this repository will be discussed +and explained in dedicated blog articles.</p> + +<p>Because iSEC has been assessing the security of AWS environment for several +years, we have a number of ideas and articles in the pipe awaiting to be +written and published. Our target goal is to publish at least on a bi-monthly +basis at the beginning of the project, and adjust this publication rate after +we catch up.</p> + +<p>Without further due, we will start this series with articles that discuss +Identity and Access Management (IAM) common issues and best practices, and will +present a strategy to improve one&#39;s security posture when using AWS.</p> + + Sun, 22 Feb 2015 22:24:00 -0800 + http://l01cd3v.github.io/aws/2015/02/22/announcing_the_aws_blog_post_series.html + http://l01cd3v.github.io/aws/2015/02/22/announcing_the_aws_blog_post_series.html + + diff --git a/index.html b/index.html index ca8a54e..56bdb54 100644 --- a/index.html +++ b/index.html @@ -1,45 +1,845 @@ ---- -layout: default ---- + + + + + + + + + + + + + + + + l01cd3v.github.io + - + +
+
+
+

l01cd3v.github.io

+

by Loïc

+ +
+
+
+
- {% for post in paginator.posts %} + - {% include post_body.html %} +
- {% if forloop.last %} - {% comment %} No separator {% endcomment %} - {% else %} + + +

+ + Post Black Hat US 2016 blog post + +

+ +
+ Published on August 8, 2016 +
+ + + + + +
+ +

Last Tuesday (August 3rd), I presented "Access Keys Will Kill You Before You +Kill The Password" at Black Hat US 2016. The summary is on the Black +Hat website and the updated slide deck is available Here. +This presentation aimed at highlighting risks associated with usage of AWS API +access keys in environments that do not enforce MFA-protected API access, and +documented strategies and IAM policies to help address these risks.

+ +

On Wednesday (August 4th), I presented Scout2 at Black Hat Arsenal. During two +hours, I had the opportunity to demo Scout2 and meet users of the tool who +shared valuable feedback with me. I look forward to implementing some of the +features discussed during this event, including adding support for ECS and +finishing the new rules generator.

+ +

As a reminder, Scout2 is available on Github, feedback is appreciated, +and feature requests and pull requests are welcome. The Scout2 documentation is +available at https://nccgroup.github.io/Scout2.

+ + +
+ +
+ + +
- {% endif %} + + + + +
+ + + +

+ + Efficient review of AWS security groups' CIDR grants + +

+ +
+ Published on November 17, 2015 +
+ + + + +
+ [Originally published on NCC Group's blog] +
+ + + +
+ +

A significant challenge for companies using the cloud lies in ensuring that +their firewall rules follow the principle of least privilege. It is +extremely common nowadays to delegate management of security groups to developers, for +both production and test environments. This means that security groups and +their associated rules are managed by a much larger number of employees than +what used to be the case in non-cloud environments, where a unique, smaller +team was in charge of managing all firewall rules. Due to the more dynamic +nature of cloud-based infrastructures, companies should review their cloud +environment's firewall rules on a more frequent basis than for non cloud-based +systems. Unfortunately, this is a difficult exercise due to the large number of +CIDRs that may be whitelisted in a given AWS account. Keeping track of all +known CIDRs and what hosts or networks they represent is not easy for +employees, and is almost impossible for external auditors who must perform the +review within a limited timeframe.

+ +

In this post, I will document how this issue can be addressed using the +AWS-Recipes tools and +Scout2.

+ +

Feed custom ip-ranges files to Scout2

+ +

Today, I am excited to announce that Scout2 accepts JSON files that +contain known CIDRs along with arbitrary metadata such as the host or network +they represent. When provided with such files, Scout2's report displays the +"friendly name" of each known CIDR that is whitelisted in security group rules. +This means that, instead of reviewing a list of obscure IP ranges, users of +Scout2 may now rely on the name associated with each CIDR.

+ +

In order to use this new feature, Scout2 should be run with the following +arguments:

+
./Scout2.py --profile nccgroup --ip-ranges ip-ranges-nccgroup.json ip-ranges-ncc-offices.json --ip-ranges-key-name name
+
+

In the above command line, Scout2 receives two ip-ranges JSON files via the +"--ip-ranges" argument:

+ +
    +
  • ip-ranges-nccgroup.json, which contains the public IP addresses in the AWS IP space in use
  • +
  • ip-ranges-ncc-offices.json, which contains the public IP addresses of several offices
  • +
+ +

Furthermore, the "--ip-ranges-key-name" argument indicates which JSON field to +display as the "friendly name".

+ +

The following screenshot illustrates that, in the Scout2 report, the name of +each known CIDR is displayed. When an IP which belongs to a known CIDR is +whitelisted, the name of the corresponding CIDR is used. In this example, +5.5.5.42/32 belongs to the 5.5.5.0/24 CIDR, which is associated with the "San +Francisco" office. An "Unknown CIDR" value is displayed when an unknown value +is whitelisted.

+ +

Screenshot: Security group rules display the name of each known CIDRs

+ +

The next section of this blog post documents how users can create and manage +these ip-ranges JSON files.

+ +

Manage known CIDRs with aws_recipes_create_ip_ranges.py

+ +

With AWS releasing their public IP address ranges, I decided to create a tool +that allows creation and management of arbitrary IP address ranges using the +same JSON format. The tool is released on GitHub at +https://github.com/iSECPartners/AWS-recipes/blob/master/Python/aws_recipes_create_ip_ranges.py +and may be used in several scenarios:

+ +
    +
  • Automatically create ip-ranges files based on public IP addresses in AWS (Elastic IPs and EC2 instances)
  • +
  • Automatically create ip-ranges files based on IP addresses documented in a CSV file
  • +
  • Manually create and manage ip-ranges files
  • +
+ +

Each of these use cases is detailed in an example below, with detailed input, +commands, and output contents.

+ +

Note: In the commands below, the "--debug" argument is used to +output pretty-printed JSON, for documentation purposes.

+ +

Automatically create ip-ranges based on public IP addresses in an AWS account

+ +

First, this tool may be used to create an ip-ranges file that contains an AWS +account's elastic IP addresses and EC2 instances' public IP addresses. By doing +so, AWS users will be able to maintain a list of public IP addresses in the AWS +IP space that are associated with their resources. Assuming that AWS +credentials are configured under the "nccgroup" profile name, the command +below may be used:

+
$ ./aws_recipes_create_ip_ranges.py --profile nccgroup --debug
+
+Fetching public IP information for the 'nccgroup' environment...
+...in us-east-1: EC2 instances
+...in us-east-1: Elastic IP addresses
+...in ap-northeast-1: EC2 instances
+...in ap-northeast-1: Elastic IP addresses
+...in eu-west-1: EC2 instances
+...in eu-west-1: Elastic IP addresses
+...in ap-southeast-1: EC2 instances
+...in ap-southeast-1: Elastic IP addresses
+...in ap-southeast-2: EC2 instances
+...in ap-southeast-2: Elastic IP addresses
+...in us-west-2: EC2 instances
+...in us-west-2: Elastic IP addresses
+...in us-west-1: EC2 instances
+...in us-west-1: Elastic IP addresses
+...in eu-central-1: EC2 instances
+...in eu-central-1: Elastic IP addresses
+...in sa-east-1: EC2 instances
+...in sa-east-1: Elastic IP addresses
+
+

My test environment has one elastic IP address that is not associated +with an AWS resource, and one EC2 instance that has a non-elastic public IP. +Executing the above command results in the creation of an +"ip-ranges-nccgroup.json" file that has the following contents:

+
{
+    "createDate": "2015-11-16-22-49-27",
+    "prefixes": [
+        {
+            "instance_id": "i-11223344",
+            "ip_prefix": "1.1.1.1",
+            "is_elastic": false,
+            "name": "Test EC2 instance",
+            "region": "us-west-2"
+        },
+        {
+            "instance_id": null,
+            "ip_prefix": "2.2.2.2",
+            "is_elastic": true,
+            "name": null,
+            "region": "us-west-2"
+        }
+    ]
+}
+
+

Automatically create ip-ranges from CSV files

+ +

From experience, I know that many companies maintain a list of their +public IP addresses, along with other network configuration information, in alternate formats, such as CSV. In +order to help with the conversion, the tool supports reading CIDR information +from CSV files. The tool was designed to be flexible and allow the creation of IP +ranges from any CSV file. In this blog post, I provide two examples.

+ +

This first example demonstrates how to use the tool to build a JSON file based +on the CSV column headers. Only attributes specified on the command line will +be copied over.

+ +

Contents of test1.csv:

+
ip_prefix, discarded_value, name
+4.4.4.0/24, ncc group, NY office
+# This is a comment...
+5.5.5.0/24, ncc group, Seattle office
+
+

Command line to convert the contents of the CSV file into JSON:

+
./aws_recipes_create_ip_ranges.py --csv-ip-ranges test1.csv --attributes ip_prefix name --profile ncc-test1 --debug
+
+

Contents of ip-ranges-ncc-test1.json:

+
{
+    "createDate": "2015-11-17-10-22-42",
+    "prefixes": [
+        {
+            "ip_prefix": "4.4.4.0/24",
+            "name": " NY office"
+        },
+        {
+            "ip_prefix": "5.5.5.0/24",
+            "name": " Seattle office"
+        }
+    ]
+}
+
+

The second example demonstrates how to use the tool to parse a CSV file with +custom column names and separate columns for the base IP and subnet mask. The +"--mappings" argument determines how columns will be mapped to the JSON +file's attributes.

+ +

Contents of test2.csv

+
Base IP, Dotted Subnet Mask, Subnet Mask, Something, Name, Something else
+3.3.3.0, 255.255.255.0, /24, Value to discard, SF Office, Other value to discard
+
+

Command line to convert the contents of the CSV file into JSON:

+
./aws_recipes_create_ip_ranges.py --csv-ip-ranges test2.csv --attributes ip_prefix mask name --mappings 0 2 4 --profile ncc-test2 --skip-first-line --debug
+
+

Contents of ip-ranges-ncc-test2.json

+
{
+    "createDate": "2015-11-17-10-07-22",
+    "prefixes": [
+        {
+            "ip_prefix": "3.3.3.0/24",
+            "name": " SF Office"
+        }
+    ]
+}
+
+

Manually create and update ip-ranges

+ +

In case CIDRs were not managed in a CSV file, the tools offers an interactive +mode that may be leveraged to manually create a JSON ip-ranges file. The +following snippet illustrates how to use the tool to interactively create new +ip-ranges JSON files:

+
$ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name
+
+Add a new IP prefix to the ip ranges (y/n)? 
+y
+Enter the new IP prefix:
+5.5.5.0/24
+You entered "5.5.5.0/24". Is that correct (y/n)? 
+y
+Enter the 'name' value:
+San Francisco
+You entered "San Francisco". Is that correct (y/n)? 
+y
+Add a new IP prefix to the ip ranges (y/n)? 
+y
+Enter the new IP prefix:
+6.6.6.6/32
+You entered "6.6.6.6/32". Is that correct (y/n)? 
+y
+Enter the 'name' value:
+San Francisco
+You entered "San Francisco". Is that correct (y/n)? 
+y
+Add a new IP prefix to the ip ranges (y/n)? 
+n
+
+

Contents of ip-ranges-ncc-offices.json:

+
{
+    "createDate": "2015-11-16-22-44-38",
+    "prefixes": [
+        {
+            "ip_prefix": "5.5.5.0/24",
+            "name": "San Francisco"
+        },
+        {
+            "ip_prefix": "6.6.6.6/32",
+            "name": "San Francisco"
+        }
+    ]
+}
+
+

The tool can also automatically add new CIDRs to existing ip-ranges files:

+
$ ./aws_recipes_create_ip_ranges.py --interactive --profile ncc-offices --attributes name --debug
+
+Loading existing IP ranges from ip-ranges-ncc-offices.json
+Add a new IP prefix to the ip ranges (y/n)? 
+y
+Enter the new IP prefix:
+7.7.7.7/32
+You entered "7.7.7.7/32". Is that correct (y/n)? 
+y
+Enter the 'name' value:
+Seattle
+You entered "Seattle". Is that correct (y/n)? 
+y
+Add a new IP prefix to the ip ranges (y/n)? 
+n
+File 'ip-ranges-ncc-offices.json' already exists. Do you want to overwrite it (y/n)? 
+y
+
+$ cat ip-ranges-ncc-offices.json 
+{
+    "createDate": "2015-11-16-22-44-38",
+    "prefixes": [
+        {
+            "ip_prefix": "5.5.5.0/24",
+            "name": "San Francisco"
+        },
+        {
+            "ip_prefix": "6.6.6.6/32",
+            "name": "San Francisco"
+        },
+        {
+            "ip_prefix": "7.7.7.7/32",
+            "name": "Seattle"
+        }
+    ]
+}
+
+

Conclusion

+ +

This addition to Scout2 provides AWS account administrators and auditors with +an improved insight into their environment. Usage of this feature should result +in further hardened security groups because detection of unknown whitelisted +CIDRs and understanding of existing rules is significantly easier.

+ +

I am currently working on a major rework of Scout2's reporting engine, +which will further improve reporting and allow creation of new alerts when an +unknown CIDR is whitelisted.

+ + +
+ +
+ + + +
+ + + + +
+ + + +

+ + Redshift support added in Scout2 + +

+ +
+ Published on August 6, 2015 +
+ + + + +
+ [Originally published on NCC Group's blog] +
+ + + +
+ +

Today, I am excited to announce that support for Redshift was added in +Scout2. By default, Scout2 will fetch information about your Redshift clusters, +cluster parameter groups, and cluster security groups if you still use +EC2-Classic. At this stage, Scout2 comes with six Redshift security rules that +are enabled by default:

+ +
    +
  • Clusters + +
      +
    • Check whether version upgrade is enabled
    • +
    • Check whether the cluster is publicly accessible
    • +
    • Check whether database encryption is enabled
    • +
  • +
  • Cluster parameter groups + +
      +
    • Check whether SSL/TLS is required to access the database
    • +
    • Check whether user activity logging is enabled
    • +
  • +
  • Cluster security groups (EC2-classic) + +
      +
    • Check whether the security group allows access to all IP addresses (0.0.0.0/0)
    • +
  • +
+ +

Scout2 was first released over a year and a half ago, and proved to be extremely +helpful when performing AWS configuration reviews. While +Scout2's initial release only supported three services (IAM, EC2, and S3) and +included thirteen security checks, the tool rapidly grew to add support for RDS +and CloudTrail. Furthermore, the tool now offers over fifty tests throughout +these five supported services. I hope that support for Redshift will bring +value to users of Scout2, and welcome feature requests, bug reports, and +recommendations on Github at +https://github.com/iSECPartners/Scout2/issues.

+ + +
+ +
+ + + +
+ + + + +
+ + + +

+ + Introducing opinel: Scout2's favorite tool + +

+ +
+ Published on August 3, 2015 +
+ + +
+ [Originally published on iSEC Partners's research blog] +
+ + + + + +
+ +

With boto3 being stable and generally available1, I took the opportunity +to migrate Scout2 and AWS-recipes to boto3. As part of that migration +effort, I decided to publish the formerly-known-as AWSUtils repository -- used +by Scout2 and AWS-recipes -- as a python package required by these tools, +rather than requiring users to work with Git submodules. I've also added more +flexibility when working with MFA-protected API calls and improved versioning +across the project.

+ +

opinel

+ +

To avoid name conflicts, I decided to rename the shared AWSUtils code to a +less misleading name: opinel. The opinel package is published on PyPI, and thus can +be installed using pip and easy_install. The corresponding source code is still +open-sourced on Github at https://github.com/iSECPartners/opinel. +As a result, Scout2 and AWS-recipes have been modified to list opinel as a +requirement, which significantly simplifies installation and management of this +shared code.

+ +

Support for Python 2.7 and 3.x

+ +

Because boto3 supports both Python2 and Python3, I decided to make sure that +the code built on top of that package has similar properties. As a result, +the latest versions of Scout2 and AWS-recipes support Python 2.7 and 3.x. +Note that opinel will NOT work with Python 2.6.

+ +

Modification of the MFA workflow

+ +

As requested by a user of AWS-recipes2, I modified the workflow when +using MFA-protected API access to no longer store the long-lived credentials +in a separate file. As a result, the .aws/credentials.no-mfa file is no +longer supported and all credentials are stored in the standard AWS credentials +file under .aws/credentials. Usage of the existing tools remains unchanged, +but the long-lived credentials are now accessible via a new profile name: +profile_name-nomfa. This allows users to work with both STS and long-lived +credentials if need be.

+ +

If you already had configured your environment to work with MFA-protected API +access, you will need to copy your long-lived credentials back to the +.aws/credentials file. This can be done with a simple command such as the +following:

+
cat ~/.aws/credentials.no-mfa | sed -e 's/]$/-nomfa]/g' >> ~/.aws/credentials
+
+

Support to use assumed-role credentials

+ +

With this new workflow implemented, I created a new recipe that allows +configuration of role-credentials in the .aws/credentials file. When the following +command is run, it uses the credentials associated with the isecpartners +profile to request role credentials for the IAM-Scout2 role. The role +credentials are then written in the .aws/credentials file in a new profile +named isecpartners-Scout2, which is the profile name appended by the role +session name.

+
$ ./aws_recipes_assume_role.py --profile isecpartners --role-arn arn:aws:iam::AWS_ACCOUNT_ID:role/IAM-Scout2 --role-session-name Scout2
+
+

Users can then use their favorite tools that support profiles. For example, +Scout2 could be run with the following command line:

+
$ ./Scout2.py --profile isecpartners-Scout2
+
+

Note that this recipe supports MFA if the assumed role requires it:

+ +
    +
  • If you never configured your environment to work with MFA, you can provide your MFA serial number (ARN) and current token code as arguments.
  • +
  • If you already configured your environment to work with MFA and stored your MFA serial in the .aws/credentials file, you just need to pass your token code as an additional argument.
  • +
  • Finally, if you already initiated an STS session, you do not need to provide a new token code and can run the command as above.
  • +
+ +

Conclusion

+ +

With the release of opinel, I hope to simplify distribution and management of +the code shared between Scout2 and AWS-recipes. Additionally, I +significantly modified the workflow and credentials storage when working with +MFA-protected API calls, which allows users to use both their long-lived and STS +credentials.

+ + +
+ +
+ + + +
+ + + + +
+ + + +

+ + IAM user management strategy (part 2) + +

+ +
+ Published on June 9, 2015 +
+ + +
+ [Originally published on iSEC Partners's research blog] +
+ + + + + +
+ +

The previous IAM user management strategy post discussed how +usage of IAM groups enables AWS administrators to consistently grant privileges +and enforce a number of security rules (such as MFA-protected API access). This +blog post will build on this idea by introducing category groups and +documenting new tools to improve IAM user management.

+ +

Categorize your IAM users

+ +

For a variety of reasons, applying a single set of security rules to all IAM +users is not always practical. For example, because many applications running +in AWS predate IAM roles, numerous environments still rely on the existence of +headless IAM users. Additionally, third parties may be granted access to an AWS +account for a number of reasons but may not be able to comply with the same set +of security rules that employees follow. For this reason, NCC recommends using +category groups to sort IAM users and reliably enforce appropriate security +measures. For example, one group for all human users and a second for all headless users may be +created: MFA-protected API access and password management are not relevant for +headless users. Furthermore, human users may be categorized into several groups +such as employees and contractors: API access can be restricted to the +corporate IP range for employees but might not be achievable for contractors.

+ +

Note 1: The set of category groups should define all types of IAM users that +may exist in your AWS account and each IAM user should belong to one -- and +only one -- category group (they may belong to other groups though).

+ +

Note 2: The common group and category groups should be used to enable enforcing +security in one's AWS environment. Policies attached to these groups should be +carefully reviewed and grant the minimum set of privileges necessary for this +type of IAM user (e.g. credential management for humans).

+ +

Example of category groups

+ +

The rest of this article describes a number of tools developed and used by +NCC to help implement this IAM user management strategy. These tools can be found +in the AWS-Recipes repository. We will +use our test AWS environment as an example, in which we use three category groups in +addition to the AllUsers common group:

+ +
    +
  1. AllHumans, the group all employees must belong to.
  2. +
  3. AllHeadlessUsers, the group all headless IAM users must belong to.
  4. +
  5. AllMisconfiguredUsers, a placeholder for sample misconfigured users.
  6. +
+ +

We also have an IAM user naming convention that requires usernames to match the +following schema:

+ +
    +
  1. Employees: firstname initial appended with lastname
  2. +
  3. Headless user: name of the service prefixed with HeadlessUser-
  4. +
  5. Misconfigured: description of the misconfiguration prefixed with MisconfiguredUser-
  6. +
+ +

Based on these rules, we created a configuration file stored under +.aws/recipes/isecpartners.json, with isecpartners matching the profile's +name. If you do not use profiles, the configuration will be under +.aws/recipes/default.json.

+
{
+    "common_groups": [ "AllUsers" ],
+    "category_groups": [
+        "AllHumanUsers",
+        "AllHeadlessUsers",
+        "AllMisconfiguredUsers"
+    ],
+    "category_regex": [
+        "",
+        "^Headless-(.*)",
+        "^MisconfiguredUser-(.*)"
+    ],
+    "profile_name": [ "isecpartners" ]
+}
+
+

This configuration file declares the name of the common IAM group and two lists +related to the categorization of IAM users:

+ +
    +
  1. A list of category groups.
  2. +
  3. A list of regular expressions matching our naming convention.
  4. +
+ +

Note 1: If you do not have a naming convention in place to distinguish the +type of user, remove the category_regex attribute from your configuration +file.

+ +

Note 2: If a regular expression is only applicable to a subset of category +groups, you must ensure that both lists have the same length and use an empty +string for groups that cannot be automatically associated (see the +AllHumanUsers group in our example).

+ +

Note 3: Use of a configuration file is not necessary as all values may be +passed as command line arguments. If a configuration file exists and a value is +passed as an argument, the value passed via the command line will be used.

+ +

Create your default groups with aws_iam_create_default_groups.py

+ +

The purpose of this tool is to create IAM groups whose name matches the common +and category groups specified in the above configuration file. Running the +following command results in four new groups being created if they did not +already exist.

+
./aws_iam_create_default_groups.py --profile isecpartners
+
+

(Automatically) sort IAM users with aws_iam_sort_users.py.

+ +

This tool iterates through all IAM users and attempts to automatically detect +the IAM groups each user should belong to. For convenience, we recommend adding +the following to your AWS recipes configuration files:

+
"aws_sort_users.py": {
+    "create_groups": false,
+},
+"force_common_group": true
+
+

This specifies default values for additional arguments to be set when running +aws_iam_sort_users.py. Specifically, with these values, running this tool +will automatically add all IAM users to the common group AllUsers and will +not attempt to create the default groups (not necessary as we already did +this). Additionally, this tool checks that each IAM user belongs to one of the +category groups. If this is not the case and the username matches a regular +expression, the user is automatically added to the matching category group. Otherwise, a +multi-choice prompt appears to allow manual selection of the appropriate +category group.

+ +

Additional advantages of configuration files

+ +

Besides helping with simplification of these tools' usage, this new AWS-recipe +configuration file can be used across tools, allowing for more consistent +rule enforcement. For example, the +aws_iam_create_user.py. +tool uses this configuration file and applies the same business logic to add +users to the common group and appropriate category group at user creation time. In +our test environment, for example, running the following command automatically +added the new user to the MisconfiguredUser group:

+
$ ./aws_iam_create_user.py --profile isecpartners --users MisconfiguredUser-BlogPostExample
+Creating user MisconfiguredUser-BlogPostExample...
+Save unencrypted value (y/n)? y
+User 'MisconfiguredUser-BlogPostExample' does not belong to the mandatory common group 'AllUsers'. Do you want to remediate this now (y/n)? y
+User 'MisconfiguredUser-BlogPostExample' does not belong to any of the category group (AllHumanUsers, AllHeadlessUsers, AllMisconfiguredUsers). Automatically adding...
+Enabling MFA for user MisconfiguredUser-BlogPostExample...
+
+

Conclusion

+ +

While efficient and reliable management of IAM users can be challenging, using +the right strategy and tools significantly simplifies this process. Creation +and use of a naming convention for IAM users enables +automated user management and enforcement of security rules.

+ + +
+ +
+ + + + + + -{% endfor %} -{% if paginator.total_pages > 1 %} + -{% endif %} + + +
+
+
+ + + + + + + + + + + + +
+
+
+ + + + + + + + diff --git a/_site/page2/index.html b/page2/index.html similarity index 100% rename from _site/page2/index.html rename to page2/index.html diff --git a/_site/page3/index.html b/page3/index.html similarity index 100% rename from _site/page3/index.html rename to page3/index.html diff --git a/_site/projects/index.html b/projects/index.html similarity index 100% rename from _site/projects/index.html rename to projects/index.html diff --git a/projects/index.md b/projects/index.md deleted file mode 100644 index 848cdcd..0000000 --- a/projects/index.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -layout: default ---- - -# Projects - -## Cloud Security - -### AWS Scout2 - -Scout2 is a security tool designed to assist security assessments of AWS -environments. It is written in Python and uses boto (AWS SDK for Python) to -gather security-related information for several services such as IAM, EC2, and -S3. The tool is open-source and available on Github at -https://github.com/iSECPartners/Scout2. - -### AWS Recipes - -The AWS-recipes repository is a placeholder for the following: - -* Tools that facilitate daily work with AWS in a secure manner -* Tools that may help penetration testers who compromise an AWS access key -* IAM policies that improve one's security posture - -Most tools available are written in Python, but I do not exclude using other -SDKs or the CLI tool sometimes. All tools, techniques, and policies are -discussed in the [AWS blog post series]({{ site.baseurl}}/categories/aws). The -AWS recipes repository is available at https://github.com/iSECPartners/AWS-recipes. - -### AWS Utils - -AWS Utils is a Python library shared between Scout2 and tools provided in AWS -recipes. It is used as a git module in both repositories to allow code reused -accross projects. This library is open-source as well and is available on Github at -https://github.com/iSECPartners/AWSUtils. - -## Privacy - -### CookieJar - -CookieJar is a browser extension for Chrome and Opera that strips cookies out -of HTTP requests when the root domain of the target URL does not match the root -domain of the tab's URL. It has the effect of anonymizing all "silent" requests -that are sent to third parties (_e.g._ social networks) when you browse the -web. The source code is open-source and available on Github at https://github.com/l01cd3v/CookieJar. -The released extensions are available for free in each browser's extension -store: - -* Chrome : https://chrome.google.com/webstore/detail/cookie-jar/iikhngeniebocgncjooahpaojhnonkfb -* Opera : https://addons.opera.com/en/extensions/details/cookie-jar diff --git a/_site/research/index.html b/research/index.html similarity index 100% rename from _site/research/index.html rename to research/index.html diff --git a/research/index.md b/research/index.md deleted file mode 100644 index c455d5c..0000000 --- a/research/index.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -layout: default ---- - -# Research - -## AWS blog post series - -Starting February 2015, I started a series of blog posts about security in the -AWS Cloud. Each post is originally published on the iSEC Partners blog at -https://isecpartners.github.io and re-posted here. All -these posts are indexed in the [AWS Category -page]({{site.baseurl}}/categories/aws). diff --git a/slides/index.html b/slides/index.html index 21bdad7..77eb8c2 100644 --- a/slides/index.html +++ b/slides/index.html @@ -1,19 +1,89 @@ ---- -layout: default ---- + + + + + + + + + + + + + + + + l01cd3v.github.io + -

Slides

+ +
+
+
+

l01cd3v.github.io

+

by Loïc

+ +
+
+
+

Slides

-
+
IAM Roles Beyond EC2 Instances: Defense-in-Depth for AWS
October 21, 2016
-
+
Access Keys Will Kill You Before You Kill The Password
August 8, 2016
+ +
+
+
+ + + + + + + + + + + + +
+
+
+ + + + + + + +