[{"categories":null,"date":"March 6, 2026","permalink":"https://badshah.io/blog/let-attendees-say-no-to-conference-swag/","section":"blog","summary":" TL;DR: Allow attendees to politely decline the conference freebies.\nI\u0026rsquo;ve attended quite a few tech conferences in India over the years, both as a speaker/trainer and attendee.\nConferences can improve execution, meritocratic selection panels, and the ratio of sponsored to overall talks. These are mentioned as part of overall feedback to the event.\nBut one thing is rarely questioned.\nThe \u0026ldquo;swag.\u0026rdquo;\nYou know what I mean - the badge, brochure, stickers, probably a t-shirt and a bag to carry everything.\nSome conferences are more creative. They have badges with LED lights and possibly a CTF challenge, accessories (like mousepad or screen cleaner), etc.\nThese are all good, but after the event, most freebies are useless.\nThey\u0026rsquo;re too cheap to sell second-hand and too time-consuming to find giveaway recipients. These freebies just hoard in my home until I get frustrated and throw them away.\nThink about it.\nThe value of an object comes from its usefulness, either helping achieve more or solving existing problems. The only exception to this is memorable objects (like the signature of a high agency person, etc) - but most conferences and swags aren\u0026rsquo;t memorable.\nThese freebies don\u0026rsquo;t help. They\u0026rsquo;re just taking up space in my head (thinking on how to use them) and my home.\nLet me explain further:\nBadges: A badge is only useful during an event. There\u0026rsquo;s a niche category of badge collectors, maybe \u0026lt;3% of total attendees. Talking about hardware badges - not everyone is a hardware hacker. They look cool on pictures and LinkedIn, but it\u0026rsquo;s just e-waste. Accessories \u0026amp; Stickers: It depends on the accessory. What will I do with a mousepad if I\u0026rsquo;m a keyboard person? What will I do with stickers if I keep my laptop clean? Brochure: This is ridiculous. There are standees at most events showing upcoming events. It makes no sense (I feel they make no sense even during the event). Just add a QR code so attendees can see the schedule online. Why do conferences do this everytime? It\u0026rsquo;s due to misaligned interests.\nConferences are businesses that have profit and loss at the end. Attendee ticket prices alone can\u0026rsquo;t cover venue, food, speaker/trainer stays, and other expenses. Sponsors fill the gap and could make an event profitable. More sponsors mean more revenue.\nThese sponsors sponsor conferences for advertising and brand visibility. They need \u0026ldquo;real estate\u0026rdquo; space for their ~ads~ brands.\nWell, here\u0026rsquo;s the real estate: t-shirt, bag, brochure, book, pen, etc.\nMore goodies mean more advertising space for sponsors. Hence, attendees receive many free ads on top of these swags.\nThen there are people like me who use time and space (in head and home) on what to do with these objects. Most times the answer is throw away than give away.\nHow About A Win-Win Strategy? Can there be a win-win situation for both of us?\nNot talking about all attendees. Just the conference and people like me.\nI think there can be.\nJust allow participants to politely say No to what you provide. Not mandatory checkbox to tick when registering but optional when you\u0026rsquo;re getting into the venue.\nThis way, if some freebie solves my pain or gives me leverage, I\u0026rsquo;ll take it. I\u0026rsquo;m fine if there\u0026rsquo;s a company logo on it. If that object doesn\u0026rsquo;t solve pain/give leverage, then I don\u0026rsquo;t want. Let anyone else who has a real usecase for that take it.\nPS: Here\u0026rsquo;s an idea for conference organizers. How about a buffet of freebies? Each category is sponsored by one company (so you make revenue). Each attendee can pick what they find really useful (so attendees will probably use it than hoarding it). And have 1-2 volunteers to ensure folks are not abusing the buffet. 😜\n","tags":["Opinion"],"title":"Let Attendees Say No to Conference Swag"},{"categories":null,"date":"December 10, 2025","permalink":"https://badshah.io/blog/what-can-go-wrong-went-wrong/","section":"blog","summary":"I recently spoke with a close friend who\u0026rsquo;s been in the security industry for over a decade. We discovered an interesting issue we both had, but never discussed.\nBoth of us had become pessimistic.\nIt\u0026rsquo;s not terminal pessimism. It\u0026rsquo;s not like there\u0026rsquo;s no point in living, doing anything, or initiatives. But it is subtle and strong enough to hinder growth.\nLet me tell you how I got here.\nAfter college, I got my first job as a Product Security Engineer. If you don\u0026rsquo;t know this role, it\u0026rsquo;s a fancy way to say that you will secure every technology that\u0026rsquo;s needed for the product to work - web apps, mobile apps, infra, networks, more. Over a year, I developed a mental model for finding security issues: Start with basic threat modeling questions and dig deeper.\nHere are Adam Shostack\u0026rsquo;s minimal questions for threat modeling:\nWhat are we working on? What can go wrong? What are we going to do about it? Did we do a good job? Then I got promoted. More tasks, more teams, more interactions, more security reviews. Adam Shostack\u0026rsquo;s question “What can go wrong? What are we going to do about it?” had become my second self, especially the “What can go wrong?” part. It was auto-suggested by my brain. I found security blind spots and architectural issues in feature design review meetings faster than before.\nI had (and still have) a relatively successful career because of this mental model.\nBut this came at a cost. This question seeped into all my thinking - even outside work.\nMe: Should I invest? My brain: What can go wrong?\nMe: Should I move to a better place to live my fullest? My brain: What can go wrong?\nMe: Should I \u0026lt;add something important\u0026gt;? My brain: What can go wrong?\nThese seemingly simple questions have many variables that can go wrong. A major chunk of these variables are outside our control. You can\u0026rsquo;t defend yourself against most of the potential issues.\nThis auto-suggested question made me focus on obstacles and how to tackle them, rather than the goal and the reason I\u0026rsquo;m pursuing it.\nThis auto-suggested question hindered my personal growth and led to another side effect - stress. As Naval puts it, uncertainty is the root of stress.\nThe vicious cycle:\nThat\u0026rsquo;s how a question that contributed to my success in my security career hindered my growth.\nWhat to do about it? 😜 I\u0026rsquo;m still in the security industry, specifically the cloud security domain. I love solving security issues. Whenever my brain auto-suggests this question outside work, I take a deep breath and consciously think about the goal and why I\u0026rsquo;m pursuing it. It requires some effort when getting started.\nAlso looking back, I find most potential issues were just in my imagination. Reality turned out far better most times. I use this as proof to convince myself not think too much about all the things that can go wrong.\nRancho in “3 Idiots” told the solution long ago. I didn\u0026rsquo;t understand it then.\nAall izz welll!\n👋\n","tags":["Opinion"],"title":"How 'What Can Go Wrong?' Went Wrong"},{"categories":null,"date":"April 15, 2025","permalink":"https://badshah.io/case-studies/saas-aws-breach/","section":"case-studies","summary":"A growing SaaS company contacted me after a serious AWS breach. The attacker accessed staging and production accounts with administrator privileges and caused significant damage:\nCompromised databases Critical resources and backups deleted Data exfiltration By the time the CTO called me in, DevOps team contained the breach.\nThe team recreated resources in a new AWS account. But significant damage was done - production disrupted for a week (in simple terms, no revenue), 1000+ developer hours gone and data exfiltrated.\nWhile the company contained the breach, they needed help to identify the root cause and prevent similar incidents in future.\nThe company had frustrating experiences with cloud auditing companies for Cloud Incident Response, which led them to contact me through my discovery call page for an independent review of the incident\u0026rsquo;s root cause.\nWhat led to this incident? After analyzing CloudTrail logs the attacker\u0026rsquo;s initial entry to cloud environment was evident - Leaked access keys belonging to IAM user with excessive IAM permissions (AdministratorAccess).\nA few other architectural issues made it difficult to detect, contain, and investigate the incident:\nAll production resources (EC2 machines, backups, etc.) were in a single account. The production account was also the AWS Organization\u0026rsquo;s management account. Not all crucial logs were enabled, like VPC flow logs. The enabled ones, like CloudTrail management and data logs, were sent to an S3 bucket in the same production account. CI/CD systems used production IAM user credentials to deploy, but the CI/CD system was hosted in a non-production account (with lenient access control). All RDS databases were on public subnets with public IPs, and access was controlled using security groups. Two significant issues in the people \u0026amp; process part were:\nIAM credentials for applications were shared among team members. All developers\u0026rsquo; SSH keys were added to the same Linux user. Their SSH sessions and executed commands were not recorded. Due to the above architectural and process issues combined with an IAM key leak with AdministratorAccess privileges, the attacker had access to nearly everything and can laterally move from staging to production account.\nNote: These issues contributed to the breach once the attacker got access to hardcoded credentials. The SaaS company also had security best practices implemented, such as using AWS Identity Center for developer login, and enabled GuardDuty, SecurityHub, and Config.\nAttacker Tactics After investigating the incident, I found the following tactics used by attackers:\nUsed VPN IP addresses to hide their origin Used custom scripts to enumerate all resources across all regions Modified database security groups to allow ingress from their VPN IPs Reset master RDS database passwords for access Exfiltrated S3 bucket content using S3 Sync command Stopped CloudTrail and deleted logs from the CloudTrail bucket Found Deletion Protection on resources (EC2, RDS, etc), turned off the setting and then deleted the resources How Better Security Controls Could Have Hindered the Attacker The leak of a privileged IAM user\u0026rsquo;s key was the entry point to the cloud account, but the breach\u0026rsquo;s impact also lies on cloud architecture.\nFew security measures that could have prevented certain attacker tactics and/or reduced the breach\u0026rsquo;s impact are:\nDon\u0026rsquo;t attach Administrator policy to any IAM user - The path to least privilege in cloud (for any company with few years of cloud presence) begins with identifying most risky IAM entities and cutting down permissions to the most necessary ones while preventing creation of new risky IAM entities. Tools like Cloudsplaining can be a good start. Use multi-accounts instead of ABAC on a single account to isolate resources - A single account is operationally easier to manage. However, segregating prod and non-prod resources (especially S3, Route53 Zones, IAM, etc) is tricky. Implementing and maintaining an effective ABAC is more challenging than account level segregation. Use AWS Organizations service to manage multiple accounts. Create a CloudTrail Org Trail instead of individual trails - This trail saves logs from all AWS accounts to a single S3 bucket in an account designated for storing logs. Enable S3 Object Lock on logging buckets - This prevents log tampering and deletion, even by privileged users. Use a suitable S3 Object Lock mode for your requirements. Enable VPC flow, CloudTrail, and S3 logs - Logs help identify attack patterns and evaluate incident blast radius. Without logs, it\u0026rsquo;s impossible to prove or disprove any hypothesis. S3 logs are expensive, so enable them for critical buckets. Backups and logs should be sent to different AWS accounts - These resources help investigate and recover from incidents and must not be stored in accounts where incidents are anticipated. Use SCPs \u0026amp; RCPs to prevent sensitive actions and setup trusted perimeter - SCPs help prevent attacker tactics like deleting critical buckets, modifying or stopping CloudTrail, etc. RCPs help prevent unknown accounts from assuming (backdoored) IAM roles and circumventing SCPs using attacker-controlled AWS accounts. Don\u0026rsquo;t host anything on the AWS Org management account - SCPs and RCPs don\u0026rsquo;t apply on management accounts. Also, setup delegated admin to child accounts and reduce management account logins. Implement external solution for SSH access - Open Source tools like Teleport grant SSH access to EC2s and offer functionality like saving all sessions and commands executed by users. (Optional, from my experience) Don\u0026rsquo;t host databases on public subnets with public IPs - Security groups can control network access to databases and EC2 instances with public IPs, but if developers change them directly from the Console, it\u0026rsquo;s easy to misconfigure and expose to unintended CIDR ranges. If it\u0026rsquo;s not possible to migrate an existing DB from public to private, implement detection on changes to important security groups. A report with detailed analysis of incident, the contributing factors, root cause and pragmatic security controls like the above was shared with the client.\nAdditionally, a customized 90-day action plan was shared to help prevent similar breaches in the future and strengthen their AWS security.\nNotes to Security Teams GuardDuty failed to detect C\u0026amp;C servers: I found a few IPs from CloudTrail Event Logs which executed sensitive actions such as DeleteTrail. GuardDuty raised a Low Severity alert about the CloudTrail being stopped. However, it never alerted about those IPs. VirusTotal and Abuse.ch listed them as known C\u0026amp;C servers. If you have enabled GuardDuty, maintain your own threat list to get notified about traffic to C\u0026amp;C servers.\nScraping CloudTrail Events History could take a lot of time: Even though CloudTrail logs can be deleted in S3 bucket, the CloudTrail management events of last 90 days is available under CloudTrail Event History. But there\u0026rsquo;s a catch - there\u0026rsquo;s a ratelimit for LookupEvents - 2 API requests per second per region. Each API request to LookupEvents fetches 50 events. If you have millions of CloudTrail events, downloading them takes considerable time before investigation. So, always protect your CloudTrail logs in S3 buckets and make it harder to tamper them. Download logs from Event History in worst case scenario.\nWhat\u0026rsquo;s most concerning in the post-analysis of this breach?\nThis breach occurred despite the company having implemented several AWS security best practices – including AWS Identity Center, GuardDuty, SecurityHub, and Config.\n(However, I need to mention they were not properly configured.)\nIs your cloud environment hiding similar risks?\nAre you sure the cloud security controls are properly setup?\nDon\u0026rsquo;t wait for a catastrophic breach to find out.\nSchedule a discovery call today! I\u0026rsquo;ll help to:\nfind cloud architectural security issues unique to your environment detect exploitable misconfigurations implement effective cloud security controls so you don\u0026rsquo;t end up facing similar breach in your company.\n","tags":["Incident Response","Forensics"],"title":"Securing a SaaS Company's AWS Environment After a Breach"},{"categories":null,"date":"April 23, 2024","permalink":"https://badshah.io/blog/key-factor-behind-tableplus-ddos-resiliency/","section":"blog","summary":"TablePlus published a blog post on how they did nothing to handle a DDoS attack. Their blog post titled \u0026ldquo;We are under DDoS attack and we do nothing\u0026rdquo; - published at the end of March 2024 - caught my eye when it quickly reached the top of Hacker News.\nIt explained the simplicity and effectiveness of their setup—a simple monolith web application written in Rust/Golang and hosted on a VPS server. Nothing fancy—no WASM, Docker, Kubernetes, or other buzzwords (not even LLM/AI-powered 😅).\nTablePlus didn\u0026rsquo;t disclose a lot of technical details. I had no clue what API endpoints were attacked or the flow of the DDoS attack. All the blog post mentioned was: \u0026ldquo;In the last 5 days alone, [attackers] tried to download it more than 800K times - our setup file is approximately 200MB per download \u0026hellip; They have attempted to download the setup file millions of times (800,126 times in the last 5 days, totaling approximately 6 million times in the latest 30 days)\u0026rdquo;.\nRecreating the attack flow While collecting the technical breadcrumbs in the blog post, I stumbled upon a Hacker News comment suggesting attackers might be downloading their Windows app (around ~183 MB in size) multiple times. Downloading Windows binary matches the blog post\u0026rsquo;s description, \u0026ldquo;our setup file is approximately 200MB per download.\u0026rdquo;\nLet\u0026rsquo;s look at the happy flow to download the Windows TablePlus binary.\nWe visit the website. Since I\u0026rsquo;m using a MacBook, I click on \u0026ldquo;Other Downloads\u0026rdquo; and then on \u0026ldquo;Download for Windows.\u0026rdquo;\nThis button makes a GET request to the URL https://tableplus.com/release/windows/tableplus_latest, which redirects to the Windows binary\u0026rsquo;s location in the https://files.tableplus.com/ subdomain.\nThat\u0026rsquo;s it.\nEach IP in the DDoS attack might have hit the tableplus.com URL, got the redirected URL to files.tableplus.com, and downloaded the file.\nDevil in the details - Cloudflare You\u0026rsquo;ll find the following statement in the middle when reading the blog post.\nWe prefer Cloudflare R2 and its CDN over Amazon CloudFront + S3 for both cost savings and protection.\nWhat if I told you the DDoS situation was handled just because they decided to go with Cloudflare?\nRemember the subdomain files.tableplus.com where the Windows binary was stored? It\u0026rsquo;s just a DNS record pointing to Cloudflare R2. 🤦\nWhat\u0026rsquo;s the real problem with DDoS, again? DDoS attacks are nothing new. It has been there for decades now.\nDuring the pre-cloud era, the problem with DDoS was majorly exhaustion of server-side resources. If you had ten servers unable to handle traffic during an attack, your site would go down, and you would lose business from genuine customers.\nThe most significant damage from DDoS attacks was just a loss of business. Maybe you also experienced a slight increase in your IT bill due to increased bandwidth usage.\nBut now, with almost every company locked onto a cloud platform, the problem with DDoS is more than server-side resource exhaustion. It also includes excessive resource (aka auto-scaling) and bandwidth usage.\nWill backend infra auto-scales prevent downtime? In most cases, the answer is still No.\nEven with autoscaling and high resource usage during a DDoS attack, multiple reasons can cause downtime, ultimately leading to business loss.\nYour EC2s autoscale until they hit your AutoScalingGroup\u0026rsquo;s Maximum capacity. Your concurrent execution of lambda functions gets throttled after hitting the max quota of 1000 executions. Your app might scale well, but your database connectivity becomes a bottleneck, or vice versa. There can be many ways DDoS leads to downtime.\nWhat if your app is entirely built on abstracted cloud services (S3, DynamoDB, etc) that autoscales based on load?\nDDoS attacks leading to server-side exhaustion are no longer valid with abstracted cloud services. Even if DDoS occurs, the cloud services will handle the load to a great extent before the cloud provider\u0026rsquo;s security team kicks in.\nAbstracted services might prevent downtime when configured correctly and usage is under the allocated quota. But remember the elephant in the room—bandwidth usage. Cloud providers are infamous for their data egress costs. Even if you toss a DDoS attack, your cloud bill will bite you back.\nOkay. In short, DDoS attacks can significantly increase your cloud bills along with possible downtime, which may be worse for many early-stage startups than losing a few customers.\nWhat if TablePlus did it differently? Was choosing Cloudflare the critical factor in DDoS resiliency?\nLet\u0026rsquo;s experiment. What if TablePlus\u0026rsquo;s infra was hosted on AWS? What\u0026rsquo;s the impact of similar DDoS? Can TablePlus enjoy \u0026ldquo;doing nothing\u0026rdquo; during DDoS?\nA small note before continuing.\nTablePlus.com is a relatively static website. Also, TablePlus\u0026rsquo;s blog post always mentions the phrase \u0026ldquo;attempted to download.\u0026rdquo; A million attempted downloads doesn\u0026rsquo;t mean a million successful downloads. For the price calculations ahead, let\u0026rsquo;s stick to the following TablePlus screenshot: 7.8 TB of bandwidth was transferred to DDoS IPs in the UK and Germany.\nFor simplicity\u0026rsquo;s sake, let\u0026rsquo;s:\nignore the cost of the EC2 instance and any of its egress costs assume that TablePlus performant Golang/Rust binary can handle 1000s of requests without the need for EC2 autoscaling ignore the number of requests mentioned in the screenshot to AWS services like CloudFront and S3. The following is just an estimation of monetary loss. It will not be accurate as the blog post doesn\u0026rsquo;t disclose the total requests and bandwidth transferred during DDoS.\nWhat if TablePlus hosted files on S3? Storing a 200 MB binary file on an S3 bucket is quite negligible. Let\u0026rsquo;s look at S3\u0026rsquo;s data transfer charges.\nAssuming the S3 bucket is in the us-east-1 region, the data transfer cost for the first 10 TB is $0.09 per GB.\nSo, 7.8 TB of data transfer translates to 7800 GB * 0.09 = 702 USD burnt due to DDoS.\nIn case the bucket was in a different region, the cost would be different:\nJakarta (ap-southeast-3) - $0.132 per GB (1029.6 USD cash burnt) Singapore (ap-southeast-1) - $0.12 per GB (936 USD cash burnt) Melbourne (ap-southeast-4) - $0.114 per GB (889.2 USD cash burnt) What if TablePlus hosted files on S3 and added CloudFront in front of it? CloudFront data transfer cost is dependent on the region where the downloads occur. If a user downloads a file from the US, it\u0026rsquo;s $0.085 per GB.\nIf another user downloads the same file from Japan, it\u0026rsquo;s $0.114 per GB.\nIf a user downloads from Europe, it costs $0.085 per GB.\nLet\u0026rsquo;s recalculate the cost: 7.8 TB * $0.085 per GB = 663 USD cash burnt due to DDoS.\nIf they were on AWS, TablePlus might burn at least 650 USD in data transfer charges because of this DDoS attack, given that they are using CloudFront on top of S3. This rate can get even higher in AWS if you have creative solutions (Lambda invocations, Lambda@Edge, etc.).\nTablePlus saved all this money because of one critical decision - using Cloudflare - R2 for distributing binaries and CDN for caching.\nCloudflare R2 does not charge for egress bandwidth. If you host a 1 GB file on Cloudflare R2 and download it a few thousand times, you will have $0 in egress charges. So, a 7.8 TB data transfer to DDoS IPs from the UK and Germany is still $0.\nYou can configure Cloudflare CDN to cache most static content (images, JS files, etc.) so the bandwidth provided by your VPS/cloud provider isn\u0026rsquo;t exhausted during a DDoS attack.\nWhile using Cloudflare (R2 and CDN) was the critical factor that allowed TablePlus to stay calm during a DDoS attack, the blog post covers many more tips, like indexing your database, keeping the central database separate from the logs/usage database, etc.\nWhile I agree with those best practices, I can\u0026rsquo;t endorse them. They never mentioned that DDoS didn\u0026rsquo;t affect their DBs only because of the suggested tweaks. The blog doesn\u0026rsquo;t even say if the DDoS attack abused their database-interacting APIs in the first place. Despite these performance tweaks, if their infra were on AWS, their cost would have increased because they did nothing.\nWhat would I do differently? TablePlus.com majorly looks like a static site. I wonder if the webpages interact with some database in the backend before rendering.\nIf I need to host such a static website, I\u0026rsquo;ll use static website builders like Hugo or Jekyll and host it on Cloudflare Pages (instead of a VPS server). Cloudflare pages can handle unlimited requests and unlimited bandwidth at just $0. Cloudflare will take care of a DDoS attack.\nPS: With Cloudflare R2, you can safely ignore Class A and Class B operations, as their costs are minimal. Class B operations that occur when downloading objects are charged at just $0.36 / million requests only after you use the free 10 million requests/month.\nTalking about the limitations of Cloudflare Pages - if I exhaust the number of builds in the free plan, I\u0026rsquo;ll look at cost-friendly hacks like hosting the static website on Cloudflare R2.\nHope you liked my blog post. Please share this blog post with others who would find this useful.\nIf you have any doubts, feel free to reach out on LinkedIn.\n","tags":["Cloudflare","AWS","DDoS","Cloud Security"],"title":"The Key Factor Behind TablePlus's DDoS Resiliency"},{"categories":null,"date":"February 26, 2024","permalink":"https://badshah.io/blog/ultimate-guide-to-fail-at-least-privilege-cloud/","section":"blog","summary":"Least privilege is a defense-in-depth strategy that everyone talks about. While I first heard it a few years back this seemed to be a magical solution to a good number of security issues I faced.\nWikipedia defines least privilege as “every module must be able to access only the information and resources that are necessary for its legitimate purpose”.\nThis term pops up in any conversation involving authorization or its related buzzwords (Zero Trust, Microsegmentation, etc).\nDespite everyone agreeing on the importance and the need for it, I haven’t found any articles or blog posts that talk about the hurdles while going that path (especially the non-technical aspects).\nWhenever I search about this term I’m made to go down the rabbit hole to find articles that just say enable some cloud service and you’ll have the least privilege. Or buy our product and you’ll have the least privilege.\nIn the last few years working around cloud and cloud-native space, I have made multiple mistakes when trying to achieve the least privilege in the cloud. Unlike other blog posts I’ve written, this is a blog post showing all the ways I failed to implement least privilege.\nWhy publish all mistakes and failures?\nWell, my understanding of what works comes from my experience of what didn’t work. I’m publishing it so my future self and others interested in least privilege can avoid making the same mistakes again.\nLet’s get started with the mistakes I made and the lessons I learned from them.\nHow I Failed at Least Privilege Failing to see least privilege as a spectrum Everyone talks about the least privilege in the cloud. The majority of these “least privileges” are just less privileges.\nLet’s take an example of the IAM policy for an application to upload and download images on an S3 bucket.\nThe majority of security professionals I talked to are fine with policy 1 or 2 and consider them as the least privilege - as it restricts access to the application to a single bucket.\nSome might consider the 3rd policy to be the actual least privilege - as it restricts access to a single bucket and the extension of the files.\nHowever, there will be a minority of professionals who will argue that the 3rd policy is still not least privileged as there’s no restriction on the size of objects uploaded and no check on actual content type of objects that will be uploaded.\nThere’s no one correct answer that suits all. You will need to find the best position in the least privileged “spectrum”. You need to find what works for your organization.\nInterestingly, in this least privilege spectrum, you are screwed at either end.\nOn one end, there are a lot of things that can be done if the application is compromised. On the other end, developers will curse you (from the bottom of their hearts?) on the number of requests they need to raise to make minor updates to IAM policies.\nFailing to get alignment from your engineering people and process Let’s say you read some fancy article saying how one achieved the least privilege with AWS Access Advisor and you wanted to try the same.\nSounds simple.\nIf your engineering people (especially the ones taking decisions) think least privilege is affecting developer productivity, your least privilege initiative will be scrapped.\nIf no one at the top level (CISO, CTO, HOE, etc) can get a buy-in for the least privilege, your initiative will end before it\u0026rsquo;s fully rolled out.\nThere are other common ways to not achieve the least privilege.\nIf you devs are into bad engineering practices - say reusing the same IAM roles for all their applications just to avoid requests to get permissions, your initiative fails.\nIf your devs don’t use IaC to create and manage resources, your initiative fails.\nIf your engineering processes just give production access to anyone who asks for it, your initiative fails.\nBefore doing any technical automation to try to go the journey of least privilege, getting alignment is mandatory.\nEnabling strict least privilege in non-prod environments is a recipe for disaster For developers, non-production environments are virtual playgrounds. They create resources there. They test and tweak the performance. They make mistakes and learn from the mistakes. They experiment with multiple configurations before moving their features to prod.\nStarting development with the least privilege in mind can counter the development efforts. There’s a high chance that devs are unaware of the most granular IAM permissions needed for the application or the new feature to work.\nEnabling guardrails is a much better approach in non-prod environments than aiming to achieve less/least privilege. Guardrails such as region restriction, allowed EC2 instance sizes, etc can reduce the blast radius when your non-prod resources are compromised.\nA clear segregation and isolation of your prod and non-prod workloads will do wonders.\nFailing to understand that least privilege is not a guarantee against major breaches It’s intuitive to think that if you achieve the least privilege, you are immune to major breaches. Do you think the same? Don’t worry, I have been there till I realized otherwise.\nLeast privilege is not a guarantee against high-impact breaches.\nAWS IAM keys with privileges only to upload and download KYC documents can be leaked on GitHub. Your Cognito identity pool can grant you the least privileged access but your Cognito user pool could be misconfigured.\nSQL injection or IDOR in your application with “least privileges” might still allow attackers to read/write to critical data in your databases. You’ll have to pentest your applications and protect them.\nLeast privilege is a defense in depth strategy at best - not the primary defense for your applications.\nFailing to support rollbacks and exclusions in automation A few AWS folks I’ve talked to have enabled least privilege automation to fetch recommendations from AWS Access Advisor and apply Permission Boundaries or update the IAM policies altogether.\nThis does a great job in certain places. When developers deprecate features, AWS Access Advisor removes the IAM permissions that are no longer needed.\nBut it will have a fair share of exceptions.\nJust because a bucket deletion functionality was not used in the last few months, it doesn’t mean an application no longer needs it. Just because your DevOps/Infra team member didn’t use a bunch of functionalities, it doesn’t mean they will never use it in the future.\nWhen these exceptions occur, the Dev/DevOps team must have a way to roll back to previous policies. Or maybe handle such situations by momentarily allowing the addition of extra permissions via the portal with less friction.\nAlso, have you thought about this question: What\u0026rsquo;s the fallback if your CI/CD system goes down?\nAnd probably this one: Should you try to reduce the privileges of your intentionally created “Admin” IAM group containing just a handful of people?\nLessons Learned Least privilege can be a gray area in some places Have you ever wondered about the least privilege for your CI/CD systems? Especially those systems where you execute terraform to create other cloud resources.\nCI/CD systems are designed to have high privileges. So do your in-house SSO applications that grant access to others.\nI still haven’t found an answer to what can be the least privilege here.\nIn such scenarios where the least privilege is not possible, I do recommend having compensatory controls - Just-In-Time elevated access for resolving P0/P1 incidents, denying dangerous actions like deleting VPC flow logs, disabling GuardDuty, etc even if a person is an admin.\nLeast privilege in the cloud has its fair share of known unknowns and unknown unknowns If you consider VPS providers as cloud providers, there are a good number of providers today still having primitive privileges - ReadOnly, Manage, Admin, Owner across all services in their platform. Your possible least privilege on these platforms is still not the least.\nYou can’t achieve granular privileges if your cloud provider doesn’t support in the first place.\nLet’s say you want to focus on major cloud providers only - like AWS, GCP, and Azure.\nThere are known unknowns. For example, AWS Access Advisor doesn’t find the least privilege with resource-based policies. Access Advisor also doesn’t support services like Amazon SNS, Amazon API Gateway, etc.\nTo overcome these known unknowns, you will have to set up additional tooling to find the least privileges in these cases.\nThen, there are unknown unknowns. These are things like undocumented Cloudtrail APIs, protocol mutation in AWS APIs, etc. We don’t know them and even if we know, it falls on the cloud provider’s promise of the shared responsibility.\nLeast Privilege will not stop at cloud If you have been reading this blog post, I’ll try to end with this one. True least privilege will not stop at the cloud.\nRemember the 4Cs? - Cloud, Cluster, Container, and Code. The least privilege applies to almost all.\nLet’s say you achieved the least privilege in the cloud. 😊\nWhat about service accounts and their permissions in your Kubernetes cluster?\nWhat if your applications are running as privileged containers in pods? Or what if the applications are running within the “root” user account in the container?\nWhat about the privileges of DB credentials configured with your application?\nWhat about the access level to Kafka topics it publishes to or consumes from?\nThe list of questions goes on.\nApplying least privilege methodology for all at once is yet another recipe for disaster. However, take one at a time, solve it for your organization, learn from mistakes, and pick the next one to solve.\nThat’s all in this post. I hope I helped you avoid a few mistakes I made myself. See you in the next one. 👋\nIf you have any doubts/ideas/suggestions, feel free to reach out on LinkedIn.\nPS: A few reached out to me about #100DaysOfAzureSecurity and asked why they aren’t receiving any emails. Sorry. I had paused it due to personal work. Will continue publishing it from next week.\n","tags":["Least Privilege","Cloud Security","AWS"],"title":"Ultimate Guide to Fail at Least Privilege in Cloud (and the Hard Lessons I Learned)"},{"categories":null,"date":"January 23, 2024","permalink":"https://badshah.io/blog/kickstarting-in-cybersecurity-for-indian-students/","section":"blog","summary":"How to get started in cybersecurity?\nThis is the first question I get from many students attending Null Bangalore meetups, security conferences, and more.\nRecently a student reached out to me over email asking for advice on the same question. He is currently in his 2nd year of B.Tech (Electrical Engineering) and is from a prestigious (three-lettered) institute. He said he\u0026rsquo;s interested in cybersecurity and wants to evaluate it as a career path but doesn\u0026rsquo;t know a good way to get started.\nI replied to him back over email.\nGiven that this is a very frequent question and the advice is almost similar, I\u0026rsquo;m publishing this as a blog post.\nDisclaimer: This advice is from my own experiences. The strategies I have mentioned in this blog post have worked for me and helped me get job(s) in the cybersecurity domain. I highly recommend experimenting with them. If it doesn\u0026rsquo;t work, feel free to move on. Also, this is a blog post written for students studying in Indian colleges/universities. This is on \u0026ldquo;how to get started in security\u0026rdquo; and NOT \u0026ldquo;get started in security, make millions in bug bounty, become famous over a short period\u0026rdquo; post.\nNow, enjoy my advice.\nNice to e-meet you. It\u0026rsquo;s great that you are asking this question in your second year. I could have given you advice even if you are in your final year, but that would be tougher for you - as you will have your final year projects and less time to experiment and learn from mistakes.\nLet\u0026rsquo;s get started with a quite long answer.\nCybersecurity (security for shorter) is a huge domain in itself. There are lots of subdomains of this domain - pentesting, blockchain security, IoT security, forensics, etc. Matter of fact, for anything that\u0026rsquo;s created - from the basic Python code to hardware/quantum computing - security will be a part of it. So for all that ever existed and will ever exist, security will be part of it, thus making it a huge domain.\nBased on my views, I categorize subdomains of security as follows:\nMost popular: Pentesting (mostly Web App and Mobile Pentesting), Product Security, Cloud Security, Enterprise Security, DevSecOps, and SOC Hot right now: AI/LLM security \u0026amp; Blockchain security Non-technical: Infosec \u0026amp; GRC (think of it like doing work to make people outside security understand security, many times in the name of compliance and with checklists) Niche: Threat Hunting, Malware analysis, Forensics, IoT security (it might be upgraded to most popular in the next 5 years), etc You can learn and make a career out of each entity I have mentioned above and I\u0026rsquo;ve seen folks successfully do it. Each of them has their pros and cons.\nFor example, people working in non-technical (Infosec \u0026amp; GRC) don\u0026rsquo;t do pretty cool stuff in their first few years, however, they probably reach CISO level compared to technical folks (at least in Indian companies).\nPeople doing niche stuff, there\u0026rsquo;s less to no good-paying market in India, and most opportunities are in the US. People doing \u0026ldquo;hot right now\u0026rdquo; kind of jobs look cool till the coolness wave goes down. (Remember the buzzword \u0026ldquo;Big Data\u0026rdquo;?) It\u0026rsquo;s easy to break into SOC analyst job roles at the beginning of a career but a bit harder (but not impossible) to switch to other domains within security down the line.\nSo it\u0026rsquo;s up to you to decide what to work on. The best answer will depend on your interests, goals in 10 years, views, personality, etc.\nA security role that I recommend getting started with If you ask me to recommend a role to get started within security, then here you go.\nI suggest a Product Security Engineer role or a Pentester role (web app + mobile). These roles are something that will be present everywhere - from small startups to large corporations. There are a lot of opportunities for these roles across companies.\nAll other categories, its niche/not present in small-mid level startups. Unless you are very passionate about it and don\u0026rsquo;t care about lesser job opportunities, then take a plunge at it.\nAlso, I would rather recommend a Product Security Engineer role in a mid-level startup than only pentesting because Product Security engineers touch both sides (attack and defense) in a lot of parts - Web App, Mobile, Cloud, Pipelines/DevSecOps, etc. If you get started with this role it\u0026rsquo;s easy for you to figure out what you like based on the actual work and then pivot to some narrower role that you find interesting.\nIt\u0026rsquo;s one thing to say you like pentesting and finding interesting bugs, it\u0026rsquo;s another to work as a pentester and find bugs within a specific period, multiple times in a year.\nLet\u0026rsquo;s say you didn\u0026rsquo;t like the ProdSec role (and you\u0026rsquo;re clear it has nothing to do with your first company\u0026rsquo;s culture) you can try switching job roles - Pentester, Security Researcher, etc. Even in your new job role, Product Security would have taught you something that you can showcase (say convincing developers to fix bugs, analyzing the actual impact of issues rather than saying every bug is CRITICAL/HIGH, etc).\nRecommendations to get kickstarted There\u0026rsquo;s no alternative to active learning. First tricky question you need to answer is which subdomain within security domain do you really like?\nIn case you are not able to answer the question, don\u0026rsquo;t worry. Make it a daily habit to read things happening around security, find something that interests you and read more about it.\nApart from these there are some other recommendations as you are in 2nd year:\nNetwork, network, network. Visit meetups like null.community, attend conferences like Nullcon/c0c0n/BSides, etc. Networking will get you more opportunities once you pass out. If you\u0026rsquo;re an introvert at least create blogs of your own. Find a mentor. Find someone who has been doing what you are interested in. Choose your mentor wisely as anyone (including those outside the security domain, in the security domain for the last 6 months, or for the last 1 decade) can give you free advice. Once you choose, learn from their experience, style, and their own greater network. Remember, each mentor is unique. (Sharing from my experience, just talking for 10 minutes with a good mentor, you\u0026rsquo;ll get a lot of information that\u0026rsquo;s not explicitly mentioned on public blog posts.) Build your coding skills. You must be in a position to write your code and debug others\u0026rsquo; code. You don\u0026rsquo;t have to be a great programmer. There are many pentesters I know who don\u0026rsquo;t even know how to write good programs let alone scale them. But what makes them good pentesters is the way they find bugs. One other common mistake I find is trying to become the best at many (cool) programming languages - Rust, Golang, etc. Python code for proof of concept still works. Have a blog about what you do/did. It\u0026rsquo;s very important these days. If you\u0026rsquo;re an introvert, then having a blog is something mandatory (in my opinion). Don\u0026rsquo;t mix up your tech and personal blog posts - at least have a way to segregate them. (Stay away from taking shortcuts like ChatGPT/other LLMs, time will tell the cost of such shortcuts 😅) Participate in Google Summer of Code or similar. Programs like Google Summer of Code, Outreachy, Major League Hacking (MLH) Fellowship, etc give you a glimpse of collaborating on real-world projects along with a stipend. These act as a feather in your cap when applying for jobs. Have a GitHub profile. Even if it\u0026rsquo;s used just to create (genuine) issues and pull requests in popular repositories, that\u0026rsquo;s fine. GitHub or GitLab - you choose, but keep your contributions public. If you are interested, participate in CTFs/Bug Bounty. CTFs are not everyone\u0026rsquo;s cup of tea. Bug Bounty is competitive these days, try finding bugs but don\u0026rsquo;t judge yourself if you can\u0026rsquo;t find any/get them closed as duplicates. Join a reputed, product-based company that HAS a security team. Your first company can make you or break you. Your first company tends to define the salary you get down the line. Also note, you can join a company without a security team as the first security person (giving you the right to brag about your position) but trust me it will backfire as companies/organizations are different entities - your role most probably ends up doing non-fancy non-important urgent work (just to tick off some audit checklist). Find a company having a security team with a good technical person leading the team and motivated team members interested in security. Remember, motivation is contagious. Security certifications are fine, but let that not be your primary focus. You might need it only if you fail to network or even have a blog. I don\u0026rsquo;t have any generic recommendations for security certifications. Also, I\u0026rsquo;ve heard multiple Indians obtain well-known security certifications by paying proxies to take the exams on their behalf, solely to acquire the certificates. Don\u0026rsquo;t go in that direction. Such certification might get you across the recruiter\u0026rsquo;s filter, but in the technical round, you will likely fail. Your leverage is your time. As a college student in your 2nd/3rd year you have ample amount of time to learn, experiment, make mistakes, fail and relearn. Get GitHub\u0026rsquo;s Student Developer Pack, check out the offers for students, experiment with hosting applications, spinning up databases, learning CI/CD pipelines, etc. The more you experiment in your college days, the more leverage (in terms of experience and knowledge) you get when you pass out of college. Don\u0026rsquo;t drop out of college unless you are going to create a company/gain technical knowledge and work in foreign countries. Indian society\u0026rsquo;s attitude towards dropouts hasn\u0026rsquo;t changed (and might not change for a few generations). That was my response. If you like it, feel free to share it on social media.\nIf you feel this blog post adds some value, share it to your siblings or friends looking to get into security.\nFAQs 1. Can you be my mentor?\nAt the time of writing this blog post, I\u0026rsquo;m occupied with multiple experiments. I currently don\u0026rsquo;t have time required to mentor someone. Sorry. I repost LinkedIn posts from credible people who genuinely offer mentorship, you will get it if you already follow me on LinkedIn.\n2. This is stupid advise. Why should I listen to you?\nThis blog post is focused on 2nd and 3rd year students studying in Indian colleges. I can update this blog post with a lot of buts and ifs - it\u0026rsquo;ll just make it a bigger and unusable.\nThese are the techniques I\u0026rsquo;ve used to get a job in security domain outside the traditional campus placements. Oh, did I forget to say - I\u0026rsquo;m not from any prestigious 3-lettered or 4-lettered colleges or any universities having famous CTF teams.\nGive them a try, if it doesn\u0026rsquo;t work just try understanding why it didn\u0026rsquo;t work for you and move on.\n3. Wow, did you figure out these recommendations all by yourself?\n\u0026ldquo;If I have seen further it is by standing on the shoulders of Giants\u0026rdquo; ~ Issac Newton.\nThe above recommendations are a mix of those given to me by my own mentors - Anant Shrivastava, Amol Naik, etc when I was a college student.\n4. I have one specific question, how can I reach out to you?\nI\u0026rsquo;m active on LinkedIn but gradually reducing my time spend on the platform. You can send a message over there however I don\u0026rsquo;t guarantee a reply. If I get some other frequent question related to this blog post, I will update the same in this blog post and post it.\n","tags":["Cybersecurity","Advice"],"title":"Kickstarting in Cybersecurity: Strategic Advice for 2nd and 3rd Year Indian College Students"},{"categories":null,"date":"September 13, 2023","permalink":"https://badshah.io/blog/aws-waf-lesser-known-limitations-revealed/","section":"blog","summary":"AWS WAF service is an L7 firewall service offered by AWS. It\u0026rsquo;s easy to set up, seamlessly integrates with other AWS services (ALB, API Gateway, etc.), and comes with a handful of managed WAF rulesets and rate limit features.\nWhile researching AWS WAF and its latest updates, I stumbled across multiple blogs, articles, and YouTube videos talking about the basics of AWS WAF and explaining how good it is. A handful of blogs explained the disadvantages - most of them either have a product that competes with AWS WAF or give high-level information about the disadvantages.\nSo, I thought I would jot down my experience with AWS WAF (just like I did with Amazon GuardDuty or AWS WAF Bot Control).\nTL;DR:\nAWS WAF service can be easily integrated with other AWS services Disadvantages include: Not so useful overview and dashboard. Users need to deploy a custom dashboard to visualize, get insights and query data. Post body inspection limits to first 8 KB. Only Cloudfront distributions can inspect up to 64 KB - which is still shorter compared to other popular commercial WAFs. It\u0026rsquo;s hard to manage WAF rules in multiple accounts without AWS Firewall Manager. This service inturn needs other services like AWS Config enabled - which adds to the cost of WAF deployment. Not so flexible rate based rules. Ratelimits are always for 5 minute rolling window. (It has another limitation described in the blog post.) Some more nitpicked limitations: AWSManagedRulesAnonymousIpList managed rule doesn\u0026rsquo;t block AWS IPs AWS WAF can\u0026rsquo;t protect you from DDoS I\u0026rsquo;ve had the opportunity to run and experiment with AWS WAF on production. I\u0026rsquo;m talking about an end-to-end implementation of AWS WAF which handled 10k+ requests per minute on customer-facing subdomains.\nI\u0026rsquo;m writing this technical blog to help Cloud Architects and Cloud Security Engineers understand the limitations of AWS WAF and help them make informed decisions when selecting a WAF solution.\nDisadvantages of AWS WAF Not So Useful Overview and Dashboard Let\u0026rsquo;s start with the basic requirement of WAF - a dashboard where someone can understand traffic patterns, analyze requests from an attack, debug false positives, etc.\nWhen you create a Web ACL, associate WAF rules, and attach it with AWS resources - you get a minimalistic dashboard containing an overview of traffic, enabled WAF rules, etc. The \u0026ldquo;Overview\u0026rdquo; tab, as the name suggests, gives an overview of the requests. You can get a high-level view of the total count of all allowed or blocked requests and the count of requests blocked by individual rules.\nThis Overview tab is nothing more than a metrics tab.\nAs you can see from the above image, there\u0026rsquo;s not much data that can be used right away. It\u0026rsquo;s hard to answer questions like \u0026ldquo;Did all the malicious traffic generate from the same IP/ASN?\u0026rdquo;\nAWS folks understand this limitation pretty well. \u0026ldquo;What\u0026rsquo;s their solution?\u0026rdquo;, you ask.\nIt\u0026rsquo;s this - Deploy a dashboard for AWS WAF with minimal effort.\nThe above article helps create a decent WAF dashboard with less effort. But you must also know that the WAF dashboard\u0026rsquo;s cost (essentially the cost of OpenSearch, Kinesis Data Firehose, etc.) is separate. The cost is somewhat proportional to the amount of traffic you get to resources attached to Web ACL.\nPost Body Inspection Limitation UPDATE - On 8th March 2024, AWS WAF has released support for larger request body inspections. As per the blog post, AWS WAF supports 64 KB of request body. While this is available for services like API Gateway, App Runner and Cloudfront, it\u0026rsquo;s not available for Application Load Balancers (ALB) and App Sync.\nFor regional web ACLs, AWS WAF can inspect the first 8 KB of the body of a request. For CloudFront web ACLs, by default, AWS WAF can inspect the first 16 KB, and you can increase this limit in your web ACL configuration to 32 KB, 48 KB, or 64 KB.\nThis is a well known limitation of AWS WAF and can be bypassed by sending junk data appended with the attack payload to vulnerable endpoints.\nThis limitation is present with most (if not all) Cloud WAF providers. However, this 8 KB coverage of post body is a bit lesser when compared to them.\nIf you stick to AWS WAF for some reason and want it to inspect a larger chunk of the post body (up to 64KB), you will need to change your web app architecture by adding CloudFront. In case you do this, you will need to re-evaluate your threat model (cough caching).\nAWS also offers post body oversize handling option - which allows you to log/block the request if the post body is over the 8 KB size. You need to be mindful when setting this option to ensure you don\u0026rsquo;t block genuine business-critical traffic in the name of security.\nAWS Firewall Manager - yet another way to spend money AWS recommends using multiple AWS accounts. It provides a logical separation of resources. In case you have multiple production accounts, maybe based on products, then this limitation is for you.\nIt\u0026rsquo;s hard to manage WAF rules for multiple accounts unless you use another service - AWS Firewall Manager. This service helps you centrally configure and manage firewall rules across your accounts and applications in AWS Organizations.\nFirewall Manager needs AWS Config enabled - meaning you pay for configuration changes at least for the mentioned resources like CloudFront, ALB, etc.\nOh, did I forget to mention? If you want to manage WAF ACLs across multiple regions, AWS Firewall Manager just doesn\u0026rsquo;t support it. You will need to create a firewall manager policy in each region where you operate.\nNot So Flexible Ratelimit Rules - Has A Major Limitation UPDATE - On 4th March 2024, AWS WAF has released support for additional time windows in rate-based rules. As per the blog post, customers can now select time windows of 1 minute, 2 minutes or 10 minutes, in addition to the previously supported 5 minutes. AWS WAF also evaluates the rate of requests about every 10 seconds. This has reduced the risk - attackers can send unlimited requests per IP in a 10 second interval before ratelimit kicks in (compared to 30 seconds window earlier).\nBased on my experience with WAFs, I have formed the opinion that ratelimit logic is meant for applications; not WAFs. The logic is simple - WAFs don\u0026rsquo;t have context. It doesn\u0026rsquo;t understand what a user is, how to differentiate paid users from unpaid users, etc. But your applications can differentiate.\nYou can make the effort to implement desired ratelimits in WAF, but you will often see it\u0026rsquo;s impossible to set up complex ratelimit logic for your business.\nAt the same time, ratelimit feature on WAFs can\u0026rsquo;t be ignored altogether.\nDuring incidents like bruteforce attacks, product abuse, etc - ratelimit feature plays a major role. If you have lots of legacy code that was deployed without security threat modeling, then the ratelimit feature is a MUST HAVE. You can write a ratelimit rule to reduce the bad traffic to your backend services - giving your engineers time to make changes during incidents.\nOn AWS WAF, ratelimit intervals are not flexible - it\u0026rsquo;s always a rolling window of 5 minutes. And the minimum number of requests in the 5-minute interval is 100.\nIf you are planning to have rate rate-based rule like allowing only \u0026lt; 100 requests per 5 minutes or like 1000 requests in 24 hours, it\u0026rsquo;s impossible.\nThe biggest limitation is how AWS WAF counts the number of requests. As per the documentation:\nAWS WAF checks the rate of requests every 30 seconds, and counts requests for the prior 5 minutes each time.\nThis means an attacker can send any number of requests in the 30-second interval before the ratelimit kicks in. In the age of nuclei and turbo intruder, this is a major limitation.\nEven if I have a ratelimit rule - 100 requests per 5-minute interval, an attacker could have sent 1000s of requests in the 30-second interval before the ratelimit rule blocked it.\nWAF Logs might contain sensitive data AWS WAF has a feature to sample requests for each WAF rule. With this request sampling feature enabled, you get some requests available under the \u0026ldquo;Sampled requests\u0026rdquo; section. It doesn\u0026rsquo;t give you all the requests, but up to 100 requests that match each rule.\nMore often than not, you will enable the WAF logging feature to store all requests to get detailed insights about the traffic, attacks, etc., or for other reasons (like regulatory compliance).\nAWS WAF logging logs most parts of the request - request method, path, query parameters, and headers. It doesn\u0026rsquo;t log the POST body.\nThis means sensitive headers like Authorization, WWW-Authenticate, Cookie, X- headers, etc. are logged. Also, if you have bad engineering practices like sending user or session data on GET query parameters - that will also be logged.\nEnabling WAF logs with the default configuration and storing them somewhere like an S3 bucket itself hurts your organization\u0026rsquo;s security. Any leak of those logs can be a disaster.\nOne must take extra effort to validate traffic/logs and update Field Redaction so the sensitive values of fields (headers and query params) are redacted in logs.\nThese are the limitations I have found with AWS WAF after deploying on production. If you are still reading the blog, it means you are serious about understanding the limitations of AWS WAF. I have got some more bonus points (nitpicked limitations) just for you.\nBonus 1: AWSManagedRulesAnonymousIpList managed rule doesn\u0026rsquo;t block AWS IPs If you don\u0026rsquo;t expect user traffic from public cloud providers to your customer-facing subdomains, AWS WAF has a managed rule called \u0026ldquo;AWSManagedRulesAnonymousIpList\u0026rdquo;. This rule detects Anonymous IPs (tor nodes, temporary proxies, etc.) and Hosting Provider IPs (cloud provider IPs).\nWeirdly this rule doesn\u0026rsquo;t block traffic from AWS IPs.\nBonus 2: AWS WAF can\u0026rsquo;t protect you from DDoS You can have a strictest ratelimit rules and enable most managed rules that block malicious traffic (which contains malicious payloads or originates from bad reputational IPs).\nAn attacker still has the advantage. This is due to the 30-second ratelimit limitation and abundant sites offering IPs for attack (say open proxies, DDoS as a service, cloud providers like DO 😜, etc.).\nAn attacker could send huge traffic (1000s req per second) of simple HTTP GET requests to your website in 30-second intervals from different IPs. The traffic will be blocked after a 30-second window for each IP. However, the damage will be done. Your applications would have stopped affecting your application availability to genuine users. When this happens as a DDoS over multiple IPs, then the continuous intermittent bursts of traffic will have taken down your backend.\nFinal Thoughts AWS WAF has both advantages and limitations. This blog post has highlighted most limitations I found with the service. This doesn\u0026rsquo;t stop you from using AWS WAF altogether.\nI still recommend AWS WAF in the following situations:\nYou are trying to block script-kiddies from running their scripts against your sites You don\u0026rsquo;t plan to use rate-based rules or worry about the 8 KB post body limitation You are trying to get a feel of WAFs and block most common web attacks In the above scenarios, AWS WAF is a very practical solution to start with. You don\u0026rsquo;t have to make changes to your infra apart from associating the ALBs, CloudFront distributions, etc. to the WAF ACL.\nYou will need to recheck if AWS WAF is the right solution for you if:\nYou are looking for a solution that acts both as WAF and mitigates DDoS You need a bit more complex ratelimit rules with different intervals Your applications take user input in POST body and 8 KB limitation doesn\u0026rsquo;t cover most incoming traffic If you have any doubts/ideas/suggestions, feel free to reach out on LinkedIn.\n","tags":["AWS","Cloud Security","WAF"],"title":"Beyond the Basics: AWS WAF's Lesser-Known Limitations"},{"categories":null,"date":"June 27, 2023","permalink":"https://badshah.io/blog/aws-reinforce-2023/","section":"blog","summary":"The much-awaited AWS re:Inforce 2023 videos have finally landed on YouTube. You can now pick your favorite track and watch the sessions at your own pace here - https://www.youtube.com/@AWSEventsChannel/playlists?view=50\u0026amp;sort=dd\u0026amp;shelf_id=2.\nThis year\u0026rsquo;s re:Inforce was a real treat, with talks introducing a host of new security services and features. While the number of new security services was limited, there were plenty of new security features introduced in existing services. And let\u0026rsquo;s not forget the plethora of sponsored talks that came with re:Inforce 2023.\nFriendly Reminder: All sessions have their talk ID in the YouTube videos (like APS202, IAM302, etc). All sponsored talks have a suffix of -S (like PRT211-S, TDR206-S, etc), so you can easily skip the talks if you wish.\nTL;DR:\nAWS re:Inforce 2023 videos are now available on YouTube Major themes included zero trust, data security, incident response, and customer experiences New services launched include Amazon CodeGuru Security, Amazon Security Lake, and Amazon Bedrock New features were added to Amazon Detective, Amazon Inspector, AWS WAF, Amazon S3, and DynamoDB Major Themes After binge-watching all the non-sponsored re:Inforce talks, I noticed a few recurring themes:\nZero trust and least privilege are getting even better - There were some fascinating talks on Human-to-Application communication (using AWS Verified Access), Service-to-Service communication (using VPC Lattice) and fine-grained authorization for apps (using Amazon Verified Permissions). Data Security is getting the spotlight - Whenever I chat about securing AWS with other security folks, data security often comes up last. Part of the reason is not knowing where to start. The talks in this event provide a great starting point for data security. Incident Response is being evangelized, especially with the use of Amazon Detective. This service has immense potential. Unlike Security Hub, which mostly collates data to a single place, Amazon Detective correlates findings. The only downside, I feel, is its pricing. Customer experience with AWS security services - Many talks shared the experiences of AWS customers who used the security services/features. It\u0026rsquo;s like a sneak peek into what your journey might look like if you decide to follow in their footsteps. New services Amazon CodeGuru Security (PREVIEW) - This is a SAST service that uses machine learning to automate code analysis from the IDE to production. It currently covers Java, Python, and JavaScript, with more languages on the way. In some cases, it can even provide code fixes for identified vulnerabilities. (Video) Amazon Security Lake is now generally available. It\u0026rsquo;s a one-stop-shop for data across AWS logs, other cloud providers, on-prem, and SaaS services. It optimizes all data, making it efficient to query and store for the long term. Also, Amazon OpenSearch can ingest data from Amazon Security Lake making the visualization part easier. (Video) Amazon Bedrock - This managed service lets users build and deploy generative AI models within their own accounts, following their encryption and security policies. It ensures that user data is not used to improve the models, shared with other customers, or shared with third-party model providers. (Video) New features Amazon Detective now groups Amazon Inspector and Amazon GuardDuty findings. Detective uses ML to infer relationships between findings and groups them together. This feature allows you to examine multiple activities as they relate to a single security compromise event. (Video) Amazon Inspector now supports exporting software bill of materials (SBOM) and code scanning in Lambda (now generally available). The SBOM feature provides an inventory of all your packages and applications, which can be exported in SPDX and CycloneDX formats. The Lambda code scanning feature scans for code vulnerabilities like OWASP Top 10 in your Lambda functions. (Video) AWS WAF has released a new feature \u0026ldquo;Account Creation Fraud Prevention\u0026rdquo;. This feature monitors sign-up or registration pages for unusual digital activity and blocks it based on identifiers like IP addresses, client data, request attributes, and client behavior. It also checks for browser automation, inconsistencies in client telemetry, and reputation scores. Plus, unlike WAF ACLs, the challenge and captcha actions are free for Fraud Control. (Video) Amazon S3 now supports dual-layer server-side encryption with keys stored in KMS (DSSE-KMS). Also, server-side encryption with customer-provided keys is termed as SSE-C. Both these options might be used by companies in highly-regulated industries. (Video) AWS Database Encryption SDK for DynamoDB provides client-side encryption library (PREVIEW). It supports attribute-level encryption and searchable encrypted attributes, improving performance and multi-tenancy. Currently, it\u0026rsquo;s a Java library and only for DynamoDB. (Video) The technical talk I loved the most AWS re:Inforce 2023 - Security design of the AWS Nitro System (DAP401) - This technical talk was a deep dive into the AWS Nitro System and its components like Nitro Cards, Nitro Security Chip, and Nitro Hypervisor.\nThat\u0026rsquo;s all for now, folks! I hope you find these resources as useful as I did. Please share this blog post with others who would find this useful.\nHappy learning!\nIf you have any doubts/ideas/suggestions, feel free to reach out on LinkedIn.\n","tags":["AWS","Cloud Security","re:Inforce"],"title":"My Key Takeaways from AWS re:Inforce 2023"},{"categories":null,"date":"May 11, 2023","permalink":"https://badshah.io/blog/aws-ses-and-email-spoofing/","section":"blog","summary":"AWS SES is used in multiple ways - automated reminders, marketing emails, security automation \u0026amp; alerts, etc. There\u0026rsquo;s a risk with the domain verified on SES; often overlooked. A risk that falls at the intersection of Cloud and Enterprise risk.\nTL;DR:\nAWS SES allows verifying both individual emails and domains Domain verified in AWS SES means Any IAM user having required permissions can spoof any email address in the domain By default, emails sent are not logged Spoofed emails will have 99.9+% delivery into inboxes Mitigation: Apply sending authorization policy to whitelist email addresses Email is a critical communication channel for businesses of all sizes, and AWS SES (Simple Email Service) is a popular choice for sending and receiving emails. With AWS SES, you can send millions of emails per day and scale up or down based on your needs. It allows you to send and receive emails using your email addresses and domains.\nLike other \u0026ldquo;Simple\u0026rdquo; services in AWS, SES is not a simple service. There are a lot of features available and different configurations possible with the service. So you get a lot of flexibility at the cost of simplicity.\nLet\u0026rsquo;s say you want to know about SES (maybe because it\u0026rsquo;s interesting?), just open its documentation. The documentation is also not so simple. It\u0026rsquo;s just a short structured book where the most essential parts are hidden in bits and pieces at multiple places. Once you start reading, you could do three things: quit reading the docs (most common), get lost in the rabbit hole of clicking different links (very common) or understand the basics of email security and how SES works (rare, might happen).\nDilemma of Email and Domain Verification AWS SES allows verifying two types of identities: email address and domain.\nEmail verification involves verifying individual email addresses that you plan to send emails from. This process involves sending a verification email to the email address and clicking on a link to confirm ownership of the address. Once an email address is verified, you can send emails from it using AWS SES.\nDomain verification, on the other hand, involves verifying an entire domain that you plan to send emails from. This process involves adding a few DNS records to your domain\u0026rsquo;s DNS configuration. Once the domain is verified, you can send emails from any email address that uses that domain.\nLet\u0026rsquo;s do a thought experiment: With both options available, what would you consider? Just verify a few email addresses or verify an entire domain on SES.\nI would say verifying individual emails would be the answer in most scenarios. I bet that you/your company sends emails from a handful of emails and doesn\u0026rsquo;t need the entire domain to be verified.\nBut from experience I\u0026rsquo;ve seem some customers end up verifying the entire domain - the domain which hosts their employee and group email addresses.\nThe Hidden Risk While the email deliverability is ensured and your marketing/product teams are happy, there\u0026rsquo;s a risk introduced in your AWS account - Email Spoofing.\nOnce a domain is verified on SES, anyone having SES SMTP credentials or having ses:SendEmail or ses:SendRawEmail IAM permission on the domain resource can send email from any email address.\nI mean any email address - ceo@yourdomain.com, pr@yourdomain.com, marketing@yourdomain.com, etc.\nBecause AWS is acting as your domain\u0026rsquo;s email server, any emails an attacker sends (phishing, foul language, NSFW, etc) have a 99.9+% chance of getting delivered to the recipient\u0026rsquo;s inbox. They will look legitimate even though the content might seem fishy.\nThere\u0026rsquo;s a default feature that adds fuel to this fire.\nBy default, emails sent using SES do not get logged anywhere (no Cloudtrail, no default logging, etc). Logging in itself is not optional for using the functionality. One could verify the domain and send a thousand emails successfully without setting up any kind of logging.\nIn case you want to log the emails sent using SES for long-term/audit purposes, you must add extra effort to set up SNS notifications and then probably route it to S3 bucket for logs using a handful of Amazon\u0026rsquo;s \u0026ldquo;Simple\u0026rdquo; services.\nIs it a \u0026ldquo;Cloud Security\u0026rdquo; issue? This vulnerability feature introduces a risk that falls at the intersection of Cloud and Enterprise risk.\nAs AWS SES is not under the scope of most popular Cloud Security Benchmarks (CIS, etc), I\u0026rsquo;ve not seen this issue highlighted anywhere. (Please let me know if you have read about this anywhere/in any checklist before)\nIn terms of Cloud Security Governance, this issue can fall under both CSPM and CIEM issues. CSPM because logging is missing for the verified domain in SES; CIEM because IAM entities can impersonate any email user in the verified domain.\nRemediation Use individually verified email identites in Amazon SES wherever possible.\nIf you need to have a verified domain, AWS offers a way to remediate this issue and it\u0026rsquo;s hidden towards the middle of the documentation. The solution is to apply a Sending Authorization policy on the verified domain.\nThe authorization policy would look as follows:\n{ \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Id\u0026#34;: \u0026#34;ExamplePolicy\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Sid\u0026#34;: \u0026#34;AuthorizeFromAddress\u0026#34;, \u0026#34;Effect\u0026#34;: \u0026#34;Deny\u0026#34;, \u0026#34;Principal\u0026#34;: { \u0026#34;AWS\u0026#34;: \u0026#34;111111111111\u0026#34; }, \u0026#34;Action\u0026#34;: [ \u0026#34;ses:SendEmail\u0026#34;, \u0026#34;ses:SendRawEmail\u0026#34; ], \u0026#34;Resource\u0026#34;: \u0026#34;arn:aws:ses:us-east-1:111111111111:identity/yourdomain.com\u0026#34;, \u0026#34;Condition\u0026#34;: { \u0026#34;StringNotLike\u0026#34;: { \u0026#34;ses:FromAddress\u0026#34;: [ \u0026#34;marketing@yourdomain.com\u0026#34;, \u0026#34;support+*@yourdomain.com\u0026#34;, \u0026#34;info@yourdomain.com\u0026#34;, \u0026#34;newsletter@yourdomain.com\u0026#34; ] } } } ] } Note: If you are using this policy, replace yourdomain.com and AWS account ID 111111111111 with correct values.\nThis authorization policy denies sending emails using any email address in the verified domain except the whitelisted ones.\nFor the logging part, there\u0026rsquo;s no other option. You have to set up SNS notifications for emails and send the logs to S3 via Kinesis Data Firehose - spending some money along this pipeline to achieve security and visibility.\nFinal Thoughts I feel this risk with AWS SES hasn\u0026rsquo;t got enough attention so far. The default configuration with verified domain isn\u0026rsquo;t secure and securing it costs some effort and money along the way.\nDoes this mean AWS cloud services are insecure by default, it\u0026rsquo;s an evil company looking to make money, etc?\nI don\u0026rsquo;t think so.\nI think AWS would mitigate this issue over time. It would be great to see if they add awareness prompts as it does while making S3 bucket public. AWS customers can consciously decide if it\u0026rsquo;s an acceptable risk or not if they are aware about the risk.\nHope you liked my blog post. Please share this blog post with others who would find this useful.\nIf you have any doubts, feel free to reach out on LinkedIn. Please let me if you found this SES issue as part of any checklist/blog post earlier.\nEDIT 1: This is a modified version of the first version of blog post. The first version described that AWS customers are made to verify domains in SES as individual verified emails would fail the DMARC check. However, there\u0026rsquo;s an option to add Custom MAIL FROM in SES to overcome the DMARC failure. Thanks to Mohit Singh for finding the in the documentation :)\n","tags":["AWS","Cloud Security","SES","Email Security","Enterprise Security"],"title":"The Risk You Can't Afford to Ignore: AWS SES and Email Spoofing"},{"categories":null,"date":"April 10, 2023","permalink":"https://badshah.io/blog/my-love-hate-relationship-with-cloud-custodian/","section":"blog","summary":"I\u0026rsquo;m a huge fan of the Cloud Custodian tool. If you hear the name for the first time - it\u0026rsquo;s an open-source rules engine for cloud security, cost optimization, and governance.\nAs I have deployed Custodian at work for over a year, I think it\u0026rsquo;s a good enough period to describe the experience - especially my love/hate relationship with the tool.\nNote: I use Custodian\u0026rsquo;s cloudtrail execution mode to protect AWS accounts. Some advantages and disadvantages might not apply to you when you use other modes or other cloud providers.\nTL;DR:\nLove Story Open Source and good community support Detects and can auto-mitigate issues in real-time Cost-effective Supports customizable policies No open source tool to compete with Custodian Hate Story Lacks documentation and examples More AWS accounts could mean more setup chaos Creating custom notification messages can be tricky Resource-based policies are not always fun Other open source software issues - maintenance, upgrade, monitoring, etc Love Story Just imagine a tool in your cloud security arsenal that has the following:\nOpen Source with community support Can detect misconfigurations in near real-time or at periodic intervals Supports auto-mitigation (both in real-time and periodic) Integrates with your enterprise messaging tool\u0026rsquo;s webhooks to send alerts Cost effective (\u0026lt;100 USD for 1000s of resources in 20+ AWS accounts) Customizable detection rules as per your requirement If you liked the specifications, you might just fall in love with Cloud Custodian.\nCloud Custodian is a rules engine for cloud security, cost optimization, and governance. Since it\u0026rsquo;s an \u0026ldquo;engine,\u0026rdquo; its efficiency depends on the \u0026ldquo;driver\u0026rdquo; implementing it (pun intended 😛).\nCustodian requires all misconfiguration checks defined as YAML files. Since these YAML files (aka policies) are plaintext files, you can have Compliance as Code along with your Infrastructure as Code. You can streamline the process of Custodian policy deployment using CI/CD pipelines.\nTalking about misconfiguration detection in AWS, there are tons of open-source tools out there. Custodian stands out among its peers as it allows us to define how a misconfiguration must be mitigated (in the policy itself). The mitigation is automatic and done in near real-time when using Custodian\u0026rsquo;s Cloudtrail execution mode. This feature is a game changer.\nTo put it in a single sentence: Cloud Custodian is a great tool in your Cloud Security CSPM arsenal.\nAlso, this tool has an unfair advantage - there\u0026rsquo;s no open-source alternative to it.\nI have come across a few similar tools, but they didn\u0026rsquo;t make it close to Custodian:\nairbnb/streamalert - a serverless real-time framework with alerts based on the detection logic you define. The tool is in its maintenance mode without new releases. The creator of this tool has left AirBnB to found Panther.com CRED-CLUB/DIAL - DIAL (Did I Alert Lambda?) is a centralized security misconfiguration detection framework. The tool has not got any updates, covers a handful of AWS services, and is a bit tedious to set up. Cloud Custodian\u0026rsquo;s policies are easier than AWS Config rules.\nFor example, to fetch all S3 buckets which don\u0026rsquo;t have encryption:\nCustodian Policy:\npolicies: - name: s3-bucket-encryption-off resource: s3 filters: - type: bucket-encryption state: False AWS Config rule: https://github.com/awslabs/aws-config-rules/blob/master/python/s3_bucket_default_encryption_enabled.py\nHate Story The above love story shows the advantages of Cloud Custodian. But don\u0026rsquo;t let that fool you into believing it\u0026rsquo;s the best tool. It has its fair share of disadvantages.\nAfter deploying the tool in all AWS accounts at work (both prod and non-prod) and maintaining it for over a year, I faced some situations that are neither documented nor talked about in the cloud security community.\n1. Lack of documentation \u0026amp; examples Lack of documentation is by far the major concern. The existing documentation is good but not good enough. It covers the most common setup guides, sample policies, and tools like c7n-org, c7n-mailer, etc.\nIf you are looking out for simple misconfiguration policies like \u0026ldquo;S3 bucket encryption missing\u0026rdquo;, \u0026ldquo;IAM user having console access without MFA\u0026rdquo;, or \u0026ldquo;Public EBS snapshots\u0026rdquo;, etc - you can find them online.\nBut if you are trying to implement any policy other than the most simple and obvious ones, you will have to figure it out yourself.\nYou need to check the schema of the resource using custodian schema aws.\u0026lt;service\u0026gt; and figure out which action/filter would be helpful for your requirement.\nAlso, you can\u0026rsquo;t find a single repository containing all Custodian policies for CIS benchmarks or similar. A repository like that would make the life of a Cloud Security Engineer more effortless.\nTools like CloudQuery or Steampipe win over Custodian (in detection) because they have community-curated checks in a single repository.\n2. More AWS accounts can mean more setup chaos If you have a single AWS account, setup is super easy.\nIf you have a few accounts under an AWS organization, the setup is easy. You can take the help of c7n-org to deploy lambdas across accounts for near real-time alerts/auto-mitigations.\nThe slight complexity with the deployment of c7n-mailer in a multi-account setup. c7n-mailer is a utility that allows Custodian policies (deployed as lambdas) to send notifications when it detects misconfigurations.\nThis c7n-mailer utility needs:\nan SQS queue - to get messages from policies deployed as lambdas a lambda function - to parse the messages in the queue and send notification One needs to take care of the following to make a \u0026ldquo;secure\u0026rdquo; deployment of the security tool:\nTo reduce complexity, have a central c7n-mailer SQS queue. You need to make sure your AWS accounts can send messages to the queue while other AWS accounts outside your organization can\u0026rsquo;t. c7n-mailer stores your Slack webhook/token, Splunk token, etc as plain text in the lambda function. You need to encrypt those sensitive data with a KMS key before storing it in code. There\u0026rsquo;s no automated way to deploy the above-mentioned secure setup. One has to make an effort to search \u0026amp; read documentation, make mistakes when deploying, and finally get it working.\nThis issue occurs whenever you are implementing a central component that your Custodian lambda functions interact with.\n3. Creating custom notification messages can be tricky Custodian supports sending notifications using c7n-mailer as mentioned above. The notifications can be sent over Email, Slack, Splunk, etc.\nThere are some sample notification templates in the code repository. Custodian uses Jinja2 templating engine to create the notification messages.\nIf you want to use the default messages, then you are fine.\nYou will have a hard time in case you want to add some text formatting to the message / change the way non-compliant resources are displayed in the notification.\nDebugging Jinja2 templates is tricky and very tedious. Also, if you mess up the templates, you will not get notifications.\n4. Resource-based policies are not always fun Cloud custodian policies are resource-based. A resource refers to a cloud service or its component that can be managed through policies.\nThere can be a resource covering an AWS service and a few more resources covering specific parts of the service. For example, Custodian resources for S3 includes aws.s3, aws.s3-access-point and aws.s3-access-point-multi\nWhile resources give granularity to the policies we create, at times this can mean we have to create multiple policies for a simple task.\nLet\u0026rsquo;s consider this simple task - alert me whenever there\u0026rsquo;s a GuardDuty finding.\nCustodian doesn\u0026rsquo;t consider GuardDuty or its findings as a resource. Instead, it considers GuardDuty as an execution mode under each resource like EC2, S3, EKS, etc.\nSo if I need to alert all GuardDuty findings, I have to create multiple policies for each AWS service - IAM, S3, EKS, etc.\nIf these policies are deployed as lambda functions, then there would be multiple lambda functions for a single task.\n5. Other open-source software issues This issue is not something specific to Cloud Custodian but OSS in general. When using Custodian, you must:\nMaintain it - Regularly upgrade Custodian CLI to use new features and AWS services added to it. Write and test new policies and optimize existing ones (maybe even ignore a few cloud assets in policies where the business has accepted the risk). Onboard new AWS accounts and remove old ones. Monitor it - One bad notification template for c7n-mailer is all that it takes stop notifications. You would need to set up monitoring to make sure your Custodian setup is working. Expect delayed or no support for issues - You might not get an answer for your issue immediately. You might not get a fix for your bug in the new release. (I admire Kapil Thangavelu\u0026rsquo;s effort to look into Custodian issues and respond, but I don\u0026rsquo;t think he alone can answer the queries of many people and fix bugs on priority.) There is one issue specific to this tool.\nIf you want a management report like how many misconfigurations were detected and mitigated in the last X months using Custodian, that\u0026rsquo;s impossible. Custodian doesn\u0026rsquo;t come with any UI. You have to build it on your own (which takes time).\nFinal Thoughts Cloud Custodian is a great tool in your cloud security defense arsenal. I will continue to recommend the tool till there\u0026rsquo;s a better open-source alternative.\nIf you are looking for a tool that would detect and auto-mitigate issues in lesser than 1 minute, there\u0026rsquo;s nothing like Custodian.\nWhen I started exploring and using Custodian no one told me about the disadvantages. Nor did I find any blog posts like this one. I hope this blog post helps you get a better picture of the pros and cons of using Custodian.\nIf you have any doubts, suggestions, or alternatives to this tool - feel free to DM me on LinkedIn or Twitter.\nShameless Plug\nKnowledge comes from reading documentation. Wisdom arises from hands-on experience.\nI have spent months exploring, trying, and testing open-source tools to defend AWS and evaluating them in production environments. While guides and tutorials give you a partial picture of tools, I have had the opportunity to battle-test them in the real world.\nKumar Ashwin and I have condensed our real-world experiences on Attacking and Defending AWS into a three-day hands-on training - \u0026ldquo;AWS Security Masterclass\u0026rdquo;.\nIf you are trying to step up your AWS Security or even get to know the latest tools and techniques in the market, register for the training at x33fcon, Gdynia, Poland.\n","tags":["AWS","Cloud Security","Cloud Custodian","CSPM"],"title":"My Love/Hate Relationship with Cloud Custodian"},{"categories":null,"date":"December 11, 2022","permalink":"https://badshah.io/blog/important-dependabot-feature/","section":"blog","summary":"GitHub\u0026rsquo;s Dependabot feature allows you to detect and fix vulnerabilities in code dependencies for all your repositories (public and private). Despite being a handy tool in securing software supply chain, it\u0026rsquo;s missing a very important feature.\nTL;DR:\nIf you have hundreds or thousands of dependabot alerts across your org, it\u0026rsquo;s quite intimidating on what to fix and how to move forward. Dependabot lacks a central dashboard that provides useful insights like what are the 20% vulnerable dependencies accounting for 80% of the alerts, etc. For such a dashboard you need to pull the alerts using GitHub GraphQL endpoint and visualize on your own.\nIf you haven\u0026rsquo;t heard about Dependabot, let me introduce it to you.\nIf you are using GitHub.com for your public/private repositories or using GitHub Enterprise (Cloud/Server), you can enable Dependabot and get vulnerability alerts in your dependencies (if any) for free. The best part is it has an option to automatically raise PRs if there\u0026rsquo;s a fix for existing issues.\nIt uses GitHub Advisory Database for vulnerability data. GitHub Advisory DB has good coverage of public vulnerabilities as it uses multiple sources like NVD, npm Security Advisories DB, etc to gather data.\nHowever, dependabot is not the best when it comes to tackling software supply chain vulnerabilities. For example, it very recently added transitive dependencies support for npm projects in GitHub.com. This feature is still not present in GHES.\nYou think - If it\u0026rsquo;s not the best why would you spend time on it?\nWell, good question.\nFirst of all, there\u0026rsquo;s a lot of development going on Dependabot. Every new GHES release you will see at least one Dependabot feature update. Some of the recent features include REST API for Dependabot, option to close and reopen alerts, etc. Enabling it across your GH Enterprise is just a single click away.\nYou\u0026rsquo;ll understand its value and ease-of-use when you compare it with other commercial vendors or even open-source tools like Dependency Track. There\u0026rsquo;s no need to set up integrations with your repositories, no need to host anything, and finally - no additional cost.\nSo, dependabot is a great start if you are securing your software supply chain vulnerabilities.\nThe one missing piece Enabling dependabot and tracking dependency issues/fixes for a handful of repositories is easy. If you use GHES, you can head over to https://github-ent.yourdomain.com/advisories, search involves:@me and look at the list of security advisories and their severity.\nThe problem starts when you have 100s of repositories in 10s of GitHub orgs. The above search results becomes useless. You\u0026rsquo;ll have 100s of dependency issues and each issue can affect more than one repositories.\nIn such a scenario, you will have multiple questions like:\nHow many CRITICAL/HIGH severity bugs do I have across repositories? Which GitHub org or repo has the most vulnerabilities? What are the 20% vulnerable dependencies that fix 80% of the dependabot security alerts? Am I (or my team) doing a good job fixing vulnerable dependencies? GitHub doesn\u0026rsquo;t provide any insights that helps answer the above questions. If GitHub had a central dashboard that answers the above questions, that would have been a game changer.\nGitHub has such a feature (reserved?) for GitHub Advanced Security (GHAS) customers.\nA lot of GitHub Enterprise users (who don\u0026rsquo;t use GHAS) are looking forward for a Dependabot Dashboard feature.\nLet\u0026rsquo;s Make Dependabot Great Again! I checked if there\u0026rsquo;s any tool that helps fetch dependabot alerts. I think you probably guessed it right - there\u0026rsquo;s no such tool. Even Steampipe and CloudQuery don\u0026rsquo;t support querying dependabot alerts (yet).\nIn such situations, the last resort is to check API documentation and build something on my own.\nWhen I started this visualization project GitHub didn\u0026rsquo;t provide any REST APIs. On 22 Sept 2022, GitHub provided REST API to query dependabot alerts in a public beta. On 18 Oct 2022, GitHub enabled Org level REST API to GitHub.com and GHES v3.8.\nFortunately, GitHub offers a GraphQL endpoint that can be utilized to search dependency issues in repositories. The RepositoryVulnerabilityAlert object returns the list of dependabot alerts for a repository. The object can be enhanced to include details like: the PR number of the fix for an alert, alert created and fixed/dismissed timestamps, etc.\nThe minimal GraphQL query I used looks like the following:\n{ repository(name: \u0026#34;REPO\u0026#34;, owner: \u0026#34;ORG\u0026#34;) { vulnerabilityAlerts(first:100) { pageInfo { startCursor hasNextPage endCursor } nodes { createdAt fixedAt number dependabotUpdate { pullRequest { number title mergedAt } } state dismissedAt dismisser { login } dismissReason securityVulnerability { severity advisory { ghsaId summary } package { name } } } } } } Once you fetch these data and dump them into a database, you can use any BI tools to create a dashboard.\nI have uploaded the sample code to fetch the alerts across organizations in my repo - https://github.com/0xbadshah/Dependabot-Dashboard. This python code needs to be run every day (with help of cron or scheduled GitHub Actions 😉) to fetch metrics over time. The output is stored in a Postgres DB in the database dependabot.\nThe Dependabot Dashboard you\u0026rsquo;ll need Once you have the dependabot alerts data in a DB, you can use any BI tools like Metabase, Looker, etc to create a dashboard. You can add your metrics to the dashboard.\nFor this blog post, I have used Apache Superset. The dashboard looks as follows:\nThe above image answers the questions I had:\nHow many CRITICAL/HIGH severity bugs do I have across repositories? Which GitHub org or repo has the most vulnerabilities? What are the 20% vulnerable dependencies that fix 80% of the dependabot security alerts? Am I (or my team) doing a good job fixing vulnerable dependencies? Hope you liked my blog post. Please share this blog post with others who would find this useful.\nIf you have any doubts, feel free to reach out on LinkedIn. I\u0026rsquo;m interested to learn about any automation you have done in your organization around securing software supply chain.\n","tags":["GitHub","Dependabot","DevSecOps","Dashboard","Supply Chain"],"title":"One important feature that Dependabot is missing"},{"categories":null,"date":"October 7, 2022","permalink":"https://badshah.io/blog/remove-secrets-from-git-repo/","section":"blog","summary":"Removing secrets from git repo is straightforward. With help of BFG Cleaner and privileges to force push the modified history, it\u0026rsquo;s a piece of cake.\nI believed this until I found I was partially wrong - removing something from git history doesn\u0026rsquo;t remove them from git repository\u0026rsquo;s history.\nTL;DR:\nTo delete secrets from a git repository, modifying the history and force-pushing it might not be sufficient if you use the GitHub Pull Requests feature. The PRs save a read-only copy of the code modifications. To remove the secret, you must de-reference/delete the GitHub PRs and trigger GitHub repo garbage collection to delete commits with secrets.\nHave you ever committed anything sensitive in a git repository?\nSensitive like AWS credentials, SaaS login credentials, prod DB passwords, or even a CSV file containing customers\u0026rsquo; SSNs.\nIf yes, that\u0026rsquo;s okay. All that matters to you (most probably) before committing a piece of code is that the code works. Unless we have something configured (like pre-commit hooks) to detect secrets, we can\u0026rsquo;t be aware of everything we commit into git repositories every time.\nPre-commit hooks to detect secrets are not foolproof solution. Most (if not all) tools search for specific regexes or high entropy. If you add a new regex for a particular secret type in the future and you find that such secrets are hardcoded already, you will have to revoke/rotate it or maybe remove it from git history.\nGetting back to the topic, if you have committed secrets into code and you can regenerate these secrets, take the easiest route and revoke (or rotate) it.\nFrom experience, I\u0026rsquo;ve seen folks occasionally take the red pill (removing from git history) if they:\nCommitted something like SSNs, details and email IDs of clients, etc that cannot be rotated Don\u0026rsquo;t know where the secrets have been configured in prod/dev environments (just bad secrets management) and fear those envs going down because of revocation/rotation This blog post is for those taking the red pill, especially for repos on GitHub Enterprise Server (Self-Hosted)!\nThe Story It\u0026rsquo;s storytime.\nYou might be very familiar with this.\nIt\u0026rsquo;s been a busy month. I have been working through sprints, adding new and optimizing existing code based on product requirements. The code that I was writing for the last few days finally works on the local system. 🎉🎉🎉\nI raised a PR with the code. My colleague who\u0026rsquo;s been busy debugging some issues peeks into my code, adds a few comments to improve code quality, and gives a 👍 for my PR.\nFinally, PR gets merged. Work well done!\nBut wait, I notice that I have hardcoded AWS credentials in PR #2. 🤦‍♂️\nI don\u0026rsquo;t want to revoke it because a few other developers are using the same AWS credential for some other project(s). So I add another commit to remove it.\nWe both know git remembers everything that\u0026rsquo;s committed - the hardcoded secret, the commit which introduced AWS keys, and the commit that removed it.\nI will take the extra step of removing it from git history and force-push it.\nBFG Cleaner BFG cleaner is a great tool to help remove almost anything from git history. Sensitive strings, credentials, or even complete files containing sensitive data. If you are on Mac, you can easily install it using brew install bfg.\nNOTE: A precondition to delete any sensitive string/file using BFG Cleaner is that the string/file should not be present in the HEAD commit. In this story, I\u0026rsquo;ve already deleted the AWS credential in the last commit.\nSteps to remove sensitive strings using BFG cleaner looks like this:\nClone the repository using the --mirror flag: git clone --mirror git@github.enterprise.domain.com:badshah/secret-repo.git Save the strings we need to remove - AKIAEXAMPLE123456KEY and 1111112222223333334444445555556666667777 to secrets.txt. Execute the command: bfg --replace-text secrets.txt secret-repo.git BFG will update all commits in all branches and tags but doesn\u0026rsquo;t delete any unwanted files (if any). If I wanted to delete a sensitive file in this repository, I need to execute: cd secret-repo.git \u0026amp;\u0026amp; git reflog expire --expire=now --all \u0026amp;\u0026amp; git gc --prune=now --aggressive. (In this example, as I\u0026rsquo;m just replacing sensitive text, there\u0026rsquo;s no need to execute this). Finally, force push the changes git push --force There\u0026rsquo;s some error when pushing. But let\u0026rsquo;s see if the push has removed the AWS credentials from the code.\nHurray!!\nI have successfully removed it from the git history.\nHow about the PRs?\nI think the force push must have updated them as well.\nAh\u0026hellip; I\u0026rsquo;m wrong. The git history doesn\u0026rsquo;t remember the AWS credentials. I can click on the commit ID from commit history and still see **REMOVED** in the code. The GitHub repository remembers it because the original commit is still present in the the PR #2 commit list.\nAnyone visiting the PR or the commit URL directly can still see the secrets.\nVisiting the commit from PR shows the credentials along with an error message.\nGitHub PRs GitHub allows you to create, update and close PRs. It also allows you to comment (even debate 😜) and approve PRs. But it doesn\u0026rsquo;t allow you to delete PRs even if you are the repo owner.\nThe error message in git push output told this earlier. The forced push was unable to update the \u0026ldquo;read-only\u0026rdquo; branches that belonged to GitHub PRs.\nThere\u0026rsquo;s no option to remove the \u0026ldquo;read-only\u0026rdquo; behavior of PR branches. The two helpful PR options we will have are - de-referencing and deleting.\nDe-referencing or Deleting PRs De-referencing PRs deletes the relationship between the PR and the commits it introduced. After de-referencing PRs, you can know who created the PR, at what time, and the comments made on PR but not the commits or code changes.\nDeleting PRs is more straightforward - it just deletes the PR and any associated information.\nDe-referencing and deleting PRs are actions only accessible to GitHub Enterprise Admins. If you want to remove secrets added through PRs in public/private repositories on GitHub.com, you need to contact GitHub Support.\nGetting back to the story, the next steps are:\nFind which PRs reference the particular git commit that introduced AWS creds De-reference or delete those PRs and make the commit URLs inaccessible. The following steps work if you have GitHub Enterprise Admin access and have access to the GitHub Enterprise Server via SSH. If you are following this blog post to delete a secret in your own git repository, replace github.enterprise.domain.com and badshah/secret-repo with your GH hostname and repo name.\nFinding the PRs To find PRs referencing the particular git commit that introduced the AWS secret:\nCopy the commit ID. In my case, it\u0026rsquo;s b851075285678c5234ec7416528108f148a216e5 Log in to GitHub Enterprise Server and execute the command ghe-repo badshah/secret-repo -c 'git for-each-ref --contains b851075285678c5234ec7416528108f148a216e5' You see two PRs (#2 and #3) referencing the commit with AWS creds. If it\u0026rsquo;s a repo that has a lot of active contributors and a lot of PRs, the number of PRs can be more.\nDe-referencing the PR Steps to de-reference PRs:\nSSH into the GitHub server and start the GHE console using ghe-console -y Execute the following script: repo = that \u0026#34;badshah/secret-repo\u0026#34; pr_numbers = [2, 3] prs = repo.pull_requests.select { |pr| pr_numbers.include?(pr.number) } prs.each(\u0026amp;:destroy_tracking_refs) Quit the GHE console using quit Start the GitHub garbage collection: ghe-repo-gc -v --prune badshah/secret-repo On the GitHub web app, head over to https://github.enterprise.domain.com/stafftools/repositories/badshah/secret-repo, select \u0026ldquo;Network\u0026rdquo;, and then click \u0026ldquo;Invalidate Git cache\u0026rdquo;. Once de-referenced, the PRs appear as follows:\nAlso, as the original commit that introduced the AWS secret is no longer referenced by any branches, the garbage collection deletes. Accessing the commit URL gives a 404 error.\n(Optional) Deleting the PR If you don\u0026rsquo;t want to preserve the PR information (like comments, etc.), you can delete the PR instead of de-referencing it.\nLog in to the GitHub server and start the GHE Console: ghe-console -y Execute the following script: repo = that \u0026#34;badshah/secret-repo\u0026#34; pr_numbers = [2, 3] prs = repo.pull_requests.select { |pr| pr_numbers.include?(pr.number) } prs.each do |pr| pr.destroy_tracking_refs pr.issue.destroy end Quit the GHE console using quit Start the GitHub garbage collection: ghe-repo-gc -v --prune badshah/secret-repo On the GitHub web app, head over to https://github.enterprise.domain.com/stafftools/repositories/badshah/secret-repo, select \u0026ldquo;Network\u0026rdquo;, and then click \u0026ldquo;Invalidate Git cache\u0026rdquo;. Final thoughts As I showed you in this blog post, removing something from git repo is not just modifying git history. It requires you to modify the git history, force-push the modified history, and also take extra steps to de-reference/delete PRs.\nWhile this blog post shows you how to \u0026ldquo;completely\u0026rdquo; remove secrets from GitHub repositories, using this should be the last step. Do all possible things to prevent secrets getting into git repos, say by developer awareness, pre-commit hooks, pre-receive hooks, etc.\nPS: If you have a way (other than static regex checks on pre-receive hooks) to detect or prevent secrets/sensitive data from getting into git repos, I\u0026rsquo;m very interested. Please send a DM to @bnchandrapal or email to badshah -at- badshah.io.\n","tags":["DevSecOps","Secrets","Git","BFG Cleaner","GitHub"],"title":"Did you completely remove secrets from git repository? Really?"},{"categories":null,"date":"August 14, 2022","permalink":"https://badshah.io/blog/guardduty-good-bad-ugly/","section":"blog","summary":"If you listen to anyone discussing AWS security, you probably heard about Amazon GuardDuty. It\u0026rsquo;s an intelligent \u0026ldquo;threat detection\u0026rdquo; service from AWS. It\u0026rsquo;s similar to an IDS system because it detects issues but doesn\u0026rsquo;t prevent them.\nTL;DR:\nThe Good Easy to setup It covers important services and detects a good number of issues No need to explicitly setup data sources Seamless integration with other AWS services No extra infra provisioning Supports custom trusted IP lists and threat lists The Bad Not a lot of options (hence not very flexible) It\u0026rsquo;s not foolproof and there are a few bypasses for some checks The Ugly It\u0026rsquo;s cost can get sky-high Should you enable it? Yes and No (check the conclusion 😉) GuardDuty If you ask me to describe GuardDuty\u0026rsquo;s detection capabilities in one word - I would say it\u0026rsquo;s \u0026ldquo;amazing.\u0026rdquo; It detects a variety of issues - from S3 policy allowing public read access, EC2 instances communicating with Tor nodes, and even exfiltration of data over DNS.\nIt detects these issues using different techniques and data sources. It\u0026rsquo;s documentation mentions:\nGuardDuty combines machine learning, anomaly detection, network monitoring, and malicious file discovery, utilizing both AWS-developed and industry-leading third-party sources to help protect workloads and data on AWS. GuardDuty is capable of analyzing tens of billions of events across multiple AWS data sources\u0026hellip;\nNow, you ask me the important question: Should I enable GuardDuty?\nI hope you\u0026rsquo;ll reach closer to your answer by the end of this blog post. 😉\nNOTE: This blog post is based on my experience with GuardDuty on AWS accounts (enabled for almost a year now). S3 Protection got enabled by default. I HAVEN\u0026rsquo;T experimented with Kubernetes Protection and Malware Protection yet.\nThe Good Easy to setup GuardDuty is a regional service and is recommended to be enabled in every active region. Enabling it is just a few clicks away.\nIf you have multiple accounts under AWS Organization, then enabling it across accounts needs some additional clicks. Even if you have 100 accounts, enabling it across all doesn\u0026rsquo;t take more than 10 minutes for a single region. Also, you get the option to enable/disable certain features for each account from your AWS Org management or delegated admin account.\nUnlike Amazon Inspector or AWS Systems Manager, you don\u0026rsquo;t need an agent running on EC2 instances to detect issues like malware and traffic to crypto mining servers.\nCovers important services and detects a good number of issues At the time of writing this blog post, GuardDuty detects issues/anomalies in the following services:\nAmazon IAM Amazon S3 Amazon EC2 (and EBS) Amazon EKS These services, in general, are used to store and process business-critical data. All of them (except EKS) are used by most AWS customers. GuardDuty has a good number of findings for these services.\nNo need to explicitly setup data sources GuardDuty uses different data sources to find different types of issues. The data sources include VPC flow logs, DNS query logs, CloudTrail logs, and more. You can check the full list of data sources in the official documentation.\nOnce GuardDuty is enabled, these logs are generated and analyzed on the AWS side.\nNote: These data sources will not be accessible to you on your AWS account. Let\u0026rsquo;s say entities on your VPC does thousands of DNS queries. You will get an alert if a DNS query relates to crypto mining servers. However, you can\u0026rsquo;t see the other DNS queries.\nSeamless integration with other AWS services If you are already invested in AWS Security Hub for CSPM, then you don\u0026rsquo;t need to make any extra configurations. Any issues GuardDuty detects are sent to Security Hub.\nYou can also use other AWS services to automate the actions based on the finding. Let\u0026rsquo;s say a lambda that disables an IAM user if GuardDuty raises MaliciousIPCaller finding.\nNo extra infra provisioning Let\u0026rsquo;s say you don\u0026rsquo;t want to use Amazon GuardDuty and want to build it on your own. Good. Let\u0026rsquo;s give it a try.\nSome findings of GuardDuty are easy to implement. Like BucketAnonymousAccessGranted and RootCredentialUsage. They are just static event-based findings. Just tap into CloudTrail management events using EventBridge and trigger a Lambda function depending on the event. These lambda functions check if the recently modified bucket allows public access, whether the root user logged in to AWS Console, etc.\nThe next category of findings requires a bit of computation and querying. For example: Portscan, SSHBruteForce and RDPBruteForce. These findings require you to enable data sources (like VPC flow logs), configure their storage (like sending logs to S3 in text/parquet format) and then do a continuous/periodic check for patterns (maybe using Amazon Athena).\nThe final category of findings requires a lot of effort to set up and get it working. Most anomalous behavior and exfiltration findings fall under this category. You will need to fetch all data sources and create a data lake. Then create and finetune ML models to find valid issues while keeping the false positive rate down and the false negative rate near zero.\n*author sighs deeply*\nIf you successfully did it - Congrats!\nYou have reinvented GuardDuty. You now have greater control and understanding of its functionalities.\nWhat was the cost of this setup? Thousands of man-hours, uncountable trials and errors, large infra setup, and a huge AWS bill.\nNow, compare it with enabling GuardDuty with a few clicks.\nZero infra provisioning. Logs get generated and analyzed behind the scenes. ML models and threat intel data are regularly updated behind the scenes.\nSupports custom trusted IP lists and threat lists GuardDuty has the option to whitelist/blacklist certain IPs using trusted IP lists and threat lists. Findings for these lists are generated if the IPs were found in VPC Flow logs and CloudTrail logs.\nYou can add a trusted IP list to make sure no GuardDuty alerts are generated for those IPs. One common whitelisting use case is with your network scanners running on EC2 instances. You can have only one trusted IP list per AWS account per region.\nThreat lists are the exact opposite of a trusted IP list. These lists contain IP addresses of threat actors, malware CNC servers, and more. If there\u0026rsquo;s any communication to these IPs, a finding gets generated. You can add up to 6 threat lists per AWS account per region.\nThe Bad Not a lot of options GuardDuty options don\u0026rsquo;t allow flexibility. You have few options, and that\u0026rsquo;s it. In some cases, it\u0026rsquo;s just yes or no.\nThere\u0026rsquo;s no option to:\nadd a DNS-based threat list enable S3 protection on a subset of buckets containing sensitive data reset the baseline for GuardDuty machine learning models It\u0026rsquo;s good from a beginner\u0026rsquo;s standpoint - fewer options mean lesser chances of insecure (mis)configuration.\nIt\u0026rsquo;s bad from an experienced user standpoint. I don\u0026rsquo;t have flexibility even if I know what I\u0026rsquo;m doing.\nIt\u0026rsquo;s not foolproof Its detection capabilities are great, but that doesn\u0026rsquo;t mean it can\u0026rsquo;t be bypassed.\nThere are a few public (and active) bypasses:\nAWS GuardDuty Exfiltration Bypass with VPC Endpoints - describes how UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS can be bypassed using VPC endpoints Evading AWS GuardDuty and Network Firewall using Privacy Enhancing tech - Dhruv AHUJA - describes how GuardDuty exfiltration findings can be bypassed using public DNS resolvers. The Ugly Cost can get sky-high GuardDuty\u0026rsquo;s pricing looks innocent at the first glance. The CloudTrail logs, S3 Data event logs, and EKS audit logs are priced per one million events per month. The VPC flow logs, DNS query logs, and EBS data volume are priced per GB per month.\nIn oversimplified terms, the price of GuardDuty is proportional to the number of resources and the logs they generate.\nIf you read the statement carefully, you will understand the devil in the details. 😈\nYou can always know the number of resources in your account while enabling GuardDuty. The mysterious part is the number of events/logs it generates. If you haven\u0026rsquo;t explicitly set up VPC flow logs, S3 access logs, etc you cannot estimate the amount of logs generated.\nGuardDuty\u0026rsquo;s cost estimation page doesn\u0026rsquo;t give a simple calculator to estimate costs. Instead, it recommends enabling the GuardDuty 30-day trial and checking the average daily cost. But keep in mind that the estimate is just for the existing resources and logs generated. It need not be the same one month, six months, or one year down the line.\nIf the resources and/or the amount of logs generated increase, GuardDuty price can go sky-high.\nI learned this the hard way after enabling GuardDuty with S3 protection on a data engineering account.\nIn that particular AWS account, S3 buckets were used as data lake storage (as per AWS recommendation). These buckets had a lot of data engineering tasks going on. Like AWS Glue ETL output saved to certain prefixes, adhoc Athena queries over the data, and daily aggregation queries from BI tools (using Athena connectors).\nIn 6 months, the business grew, the data generated and analyzed grew, and so did the GuardDuty pricing.\nThis graph represents the cost of GuardDuty in a single AWS account over 6 months. More than 85% of the cost was because of S3 data events.\nConclusion AWS GuardDuty is an amazing service that intelligently detects different threats across important AWS services. It\u0026rsquo;s very easy to set up on single and multi-account environments doesn\u0026rsquo;t need any infra provisioning or existing infra changes, and integrates seamlessly with other AWS services.\nWhile it\u0026rsquo;s great on some fronts, it has its fair share of issues - not very flexible, not foolproof, and can cost a bomb if the amount of logs generated or the number of resources increase over time.\nGuardDuty (without S3 protection) has a huge bang for your buck. Adding S3 protection to the equation, in general, will have good coverage but comes with a cost.\nEnough explanation so far. Now the important question - should you enable GuardDuty?\nHere\u0026rsquo;s my recommendation:\nEnable GuardDuty in all active regions across all accounts. Disable access to services in all non-active regions using SCPs. Enabling GuardDuty enables S3 protection by default. Disable the S3 protection in accounts that doesn\u0026rsquo;t contain business-critical data (like nonprod accounts). If you find S3 protection costs a lot in prod accounts during GuardDuty trial, disable S3 Protection and try to solve the issue from a different angle - maybe stringent S3 access policies, additional protection to sensitive S3 buckets, and more.\nPlease feel free to reach out to me on Twitter or LinkedIn if you have any queries. Follow me to get more insights on AWS security and other experiments.\nIf you have experimented with GuardDuty\u0026rsquo;s EKS and malware protection, I\u0026rsquo;d love to discuss it.\n","tags":["AWS","GuardDuty","Threat Detection","IDS"],"title":"GuardDuty - the Good, the Bad and the Ugly"},{"categories":null,"date":"July 29, 2022","permalink":"https://badshah.io/blog/cloudquery-vs-steampipe/","section":"blog","summary":"CloudQuery and Steampipe have very similar functionalities. The actual difference is with the way they work and the problems they solve. This blog post compares both the tools and helps you answer the question: What should I use - CloudQuery or Steampipe?\nTL;DR:\nCloudQuery and Steampipe have very similar functionalities The actual difference is with the way they work and the problems they solve CloudQuery: It is an asset inventory out of the box. It doesn\u0026rsquo;t support resource history but can dump all the latest resources to a local database using a single command. All resources are dumped to the local DB. All queries execute against local data in DB. Steampipe: It adds SQL wrapper around web services and APIs. It can be used to create an asset inventory. It comes with an embedded Postgres DB but won\u0026rsquo;t store any resource data. Resources and their configurations are fetched on-demand for each SQL query. Steampipe uses Postgres Foreign Data Wrappers to abstract the web API calls to fetch those details. Story time Scanning your public assets periodically (at least once a week) is important. Period.\nIf you use the cloud for your prod and dev environments, it\u0026rsquo;s even more important. Who knows, maybe someone just opened internal payments portal to the internet \u0026ldquo;assuming\u0026rdquo; the AWS security groups take care of network access. 🤷‍♂️\nWith that said, I was looking for a tool that helps fetch all public assets on the AWS cloud. The idea was to pass the result on to network \u0026amp; web scanners for periodic scans. I was too lazy to build a tool that fetches public assets on AWS (because if I code such a tool I have to maintain it over time).\nNOTE: I have complete read access to the AWS accounts. So, I am against the idea of running discovery tools like subfinder, amass or any other recon scripts. The problem with this \u0026ldquo;black-box\u0026rdquo; approach is it can never assure 100% coverage of all your public assets over time.\nAfter a few hours of Google searches, I found there\u0026rsquo;s no off-the-shelf tool available to fetch all public assets of AWS. I came across Project Discovery\u0026rsquo;s cloudlist tool, but it was limited to EC2 public IPs and Route53 records. Finally, I ended up with two tools that looked very similar (at least based on their home pages) - CloudQuery and Steampipe. Both tools support listing down resources on AWS using simple SQL statements. The results can segregated to get the list of public resources.\nAWS fanboys, I know what you are thinking - Why not use AWS Config? Simple. AWS Config is costly (as it\u0026rsquo;s a full-fledged asset inventory), and it doesn\u0026rsquo;t support all resource types in all regions. For example, it doesn\u0026rsquo;t monitor Cloudfront outside the us-east-1 region.\nBoth CloudQuery and Steampipe looked like the tool I needed:\nRequirements CloudQuery Steampipe Open source ✅ ✅ Fetches all assets ✅ ✅ Supports AWS Org ✅ ✅ Allows segregating public assets ✅ ✅ Easier to setup ✅ ✅ Cost effective ✅* ✅ Other programs can interact ✅ ✅ Good community support ✅ ✅ * - CloudQuery requires an external database even for initial setup which would have an additional cost.\nYou can fetch data from both tools using similar SQL statements. Sometimes the only part that changes in both input queries is the table name.\nFor example, to get a list of all publicly accessible ELBv2 instances:\nCloudQuery: SELECT * FROM aws_elbv2_load_balancers WHERE scheme = 'internet-facing'\nSteampipe: SELECT * FROM aws_ec2_application_load_balancer WHERE scheme = 'internet-facing'\nBoth these tools also have other similarities. Both tools:\nsupport Kubernetes and cloud platforms like GCP and Azure allow integration with other visualization tools (like Grafana) comes with off-the-shelf policies (like AWS CIS benchmarks, AWS foundational security best practices, etc.) So I was puzzled on why two open source tools were competing to achieve the same thing. 🤔\nAfter a few more hours of experiments with both tools, I finally understood the difference.\nCloudQuery CloudQuery is an asset inventory out of the box. You can think of it as an open-source (and cheaper) alternative to AWS Config Snapshot feature. It detects configuration details of multiple resource types along with multi-region and multi-account support.\nIts setup requires an external database to store all the resources. The installation docs show how to set up a temporary local Postgres DB over a docker container.\nOnce you install CloudQuery, set up the database, and configure AWS credentials, just execute cloudquery init aws and cloudquery fetch. You will have all the resources across all regions in your default AWS profile dumped to the local database. It \u0026ldquo;extracts\u0026rdquo; the resource data from AWS via APIs and \u0026ldquo;loads\u0026rdquo; the data to your DB.\nYou can then use the data as per your requirements. You can query the data to see if it\u0026rsquo;s compliant with CIS benchmark checks by executing cloudquery policy run aws//cis_v1.2.0. If you have any visualization tools, you can try to visualize the asset inventory. There are a few open source CloudQuery Grafana dashboards.\nThe only (major) disadvantage is it doesn\u0026rsquo;t support asset history. Let\u0026rsquo;s say five S3 buckets were deleted in the past, they won\u0026rsquo;t appear in the latest data. The support for asset history using TimescaleDB is deprecated.\nYou can have workarounds for the disadvantage, like creating backups after each run, dumping results to JSON/CSV, etc. But that\u0026rsquo;s extra work that you need to do.\nSteampipe Steampipe is a utility that abstracts web APIs and gives you a SQL interface. You can fetch all AWS resources using simple SQL queries. However, it is not an asset inventory. Every query you execute makes HTTP requests to the cloud (if there\u0026rsquo;s no data in cache).\nIt uses Postgres Foreign Data Wrappers under the hood. One can create a Steampipe plugin for almost any website and define how SQL queries would interact with the website\u0026rsquo;s APIs. These plugins take care of things like pagination and hitting multiple APIs for a single query.\nSteampipe has plugins that help fetch all your resources from the cloud. It also has mods that help visualize the fetched data and create simple compliance reports.\nOnce you install Steampipe, configure AWS credentials, just execute steampipe plugin install aws and steampipe query. Now you would have an interactive shell where you can fetch any AWS resource you want. Visualizing the data and creating compliance reports are just a few more commands away.\nSteampipe supports more service providers than CloudQuery. A good number of services that Steampipe supports have nothing to do with asset inventory (example: Shodan, Have I Been Pwned, etc).\nBut at the same time - the more plugins it has, the more advantageous it becomes.\nIf I want to create an in-house asset inventory tool that fetches resources from AWS cloud along with data on GitHub repositories and Heroku dynos, then Steampipe is the only tool that helps.\nNOTE: Steampipe can fetch the latest data on-demand. It doesn\u0026rsquo;t store the query results anywhere - you will need to explicitly save them on disk. If you wish to use Steampipe as a database for your application, it has service mode.\nA disadvantage of Steampipe is that you can unintentionally hit API ratelimits of your web services that you were querying.\nWhat should you use: CloudQuery or Steampipe? Use CloudQuery, if you want:\nan asset inventory to store all data about resources and configurations in a DB for compliance reasons to query the data thousands of times and don\u0026rsquo;t want to get rate-limited by AWS to check historical assets (this feature was deprecated but I hope it would be added in future) Use Steampipe, if you want:\nto query AWS resources on-demand a few times a day to setup simple yet powerful asset inventory dashboards (without setting up external DB and visualization tools like Grafana) to get CIS benchmarks/FedRAMP/HIPAA compliance reports with a single command to use a single query to get data from two or more services that Steampipe supports (example, using Shodan to test AWS Public IPs) Huge thanks to Bhanu Teja for helping me understand and experiment around CloudQuery.\nPlease feel free to reach out to me on Twitter or LinkedIn if you have any queries or if I missed anything that\u0026rsquo;s interesting.\n","tags":["AWS","Steampipe","CloudQuery","Asset Inventory"],"title":"What should you use - CloudQuery or Steampipe?"},{"categories":null,"date":"July 11, 2022","permalink":"https://badshah.io/blog/things-i-wish-i-knew-aws-waf-bot-control/","section":"blog","summary":"AWS WAF might be your first layer of defense for attacks on websites hosted on AWS. While WAF does its best at blocking web attacks, it doesn\u0026rsquo;t stop web abuses - like bot attacks involving API abuse. For example, submitting comments on pages, credential spraying, OTP bruteforce/resend, etc.\nThe reason for this is pretty intuitive. WAFs usually don\u0026rsquo;t have context on what\u0026rsquo;s an abuse, how the resources can get abused, and how to prevent it. So it\u0026rsquo;s up to the security engineer setting up the WAF to write custom rules to prevent such abuse.\nDo you think AWS WAF\u0026rsquo;s ratelimiting feature can help block such abuses like OTP bruteforce/resend? Pause now and think about it.\nIn most cases I\u0026rsquo;ve seen, the ratelimiting feature can\u0026rsquo;t block abuses because of its caveats. You can ratelimit to a minimum of 100 requests in 5 minutes. Not lesser than that. If you are talking about abuses like bots spraying stolen credentials or bruteforcing passwords over multiple IPs, 100 requests per 5 minutes can still do some noticeable damage.\nAfter such an API abuse incident, I wanted to check if AWS WAF\u0026rsquo;s Bot Control feature can help stop such abuse. There weren\u0026rsquo;t any useful blog posts out on the internet about the feature. So, I plan to write a blog post about it.\nThis blog post walks you through the bot control feature, its pricing, how it works, and the results after enabling it.\nWhat\u0026rsquo;s the Bot Control feature? The first few sentences of the bot control\u0026rsquo;s documentation have explained it wonderfully:\nAWS WAF Bot Control gives you visibility and control over common and pervasive bot traffic that can consume excess resources, skew metrics, cause downtime, or perform other undesired activities. With just a few clicks, you can use the Bot Control managed rule group to block or rate-limit pervasive bots, such as scrapers, scanners, and crawlers, or you can allow common bots, such as status monitors and search engines.\nPricing Bot control comes with a subscription fee of $10.00 per month per Web ACL. Additionally, there is a request fee of $1.00 for every million requests inspected.\nYou can read more about the pricing here.\nEnabling Bot Control It\u0026rsquo;s very simple to enable Bot Control.\nGoto WAF dashboard, click on the WAF ACL, head over to Rules, click \u0026ldquo;Add managed rule groups\u0026rdquo; under \u0026ldquo;Add rules\u0026rdquo; and you will see \u0026ldquo;Bot Control\u0026rdquo; in AWS managed rule groups (it\u0026rsquo;s the only paid rule group under the category 😉 ).\nToggle \u0026ldquo;Add to web ACL,\u0026rdquo; set its priority, and save the changes.\nVoila, you just enabled bot control.\nIf you deploy AWS WAF web ACL rules using JSON files, enabling it is even easier. Just copy-paste the below at the bottom of the ruleset.\n{ \u0026#34;Name\u0026#34;: \u0026#34;AWS-AWSManagedRulesBotControlRuleSet\u0026#34;, \u0026#34;Priority\u0026#34;: 1000, \u0026#34;Statement\u0026#34;: { \u0026#34;ManagedRuleGroupStatement\u0026#34;: { \u0026#34;VendorName\u0026#34;: \u0026#34;AWS\u0026#34;, \u0026#34;Name\u0026#34;: \u0026#34;AWSManagedRulesBotControlRuleSet\u0026#34; } }, \u0026#34;OverrideAction\u0026#34;: { \u0026#34;Count\u0026#34;: {} }, \u0026#34;VisibilityConfig\u0026#34;: { \u0026#34;SampledRequestsEnabled\u0026#34;: true, \u0026#34;CloudWatchMetricsEnabled\u0026#34;: true, \u0026#34;MetricName\u0026#34;: \u0026#34;AWS-AWSManagedRulesBotControlRuleSet\u0026#34; } } Note: The above rule enables Bot Control in Count mode. It doesn\u0026rsquo;t block any request upon enabling bot control (you will understand why I emphasize this later).\nHow does Bot Control work? (based on observation) First of all, bot control is a feature of AWS WAF. It respects the processing order, overriding actions of rule groups and other features of WAF web ACLs.\nWAF rule decisions made earlier in the rule processing order decide if bot control kicks in. For example, if a rule explicitly blocks a few requests for certain criteria, then the requests get dropped immediately. It doesn\u0026rsquo;t get checked for any bot signals. Every non-blocked request that reaches the bot control rule, can get labeled.\nTo see this feature in action, I enabled it on a production web ACL and set the priority as 10000 (i.e., the last rule to be processed).\nThe production web ACL usually gets ~10 million genuine requests every week. I intentionally did it so I could understand the bot control functionality better.\nHere\u0026rsquo;s what I found.\nThe requests originating from popular web browsers didn\u0026rsquo;t get labeled. So as per bot control, these are genuine requests and not bots.\nRequests from Android/iOS apps, curl requests, python/golang library requests, etc get labeled with their respective bot categories.\nExamples of Bot Labels Labels for requests from the Android app:\nawswaf:managed:aws:bot-control:bot:category:http_library awswaf:managed:aws:bot-control:bot:name:okhttp awswaf:managed:aws:bot-control:signal:non_browser_user_agent Labels for requests from random internet bruteforcer hosted on DigitalOcean:\nawswaf:managed:aws:bot-control:bot:category:http_library awswaf:managed:aws:bot-control:bot:name:go_http awswaf:managed:aws:bot-control:signal:known_bot_data_center awswaf:managed:aws:bot-control:signal:non_browser_user_agent Labels for webhook callback requests from DigitalOcean (with valid browser User-Agent):\nawswaf:managed:aws:bot-control:signal:known_bot_data_center From the above examples, you can see the labels have at least one bot signal. They can have bot categories and names to further differentiate the bot.\nJust because a request gets labeled with bot signal/category, doesn\u0026rsquo;t mean it\u0026rsquo;s bad/malicious. It can be traffic from search engine crawlers, content fetchers, and even your Android \u0026amp; iOS apps.\nLooking at all the requests and their labels over the week, I found its logic depends on a couple of factors: User-Agent and Source IP address.\nThe bot control signals can be summarized based on these two factors:\nAutomatedBrowser - if User-Agent belongs to automation frameworks (like Puppeteer, Headless Chrome, etc) KnownBotDataCenter - if the source IP address is part of bot datacenters or cloud service providers NonBrowserUserAgent - if User-Agent doesn\u0026rsquo;t belong to a valid browser or if there\u0026rsquo;s no User-Agent in the request What I learned about Bot Control? 1. Bot Control adds labels to requests while AWS WAF allows creating custom actions Bot Control does a decent job of labeling requests. You can then have a WAF rule to take custom action based on the label. For example, if the request labels have signal:NonBrowserUserAgent but don\u0026rsquo;t include name:okhttp (requests coming from an Android app), then block or rate limit it. Or even send a captcha challenge.\n2. Bot Control\u0026rsquo;s can double (or even triple) your AWS WAF bill Let\u0026rsquo;s say I have a WAF ACL with three AWS Managed Rule groups enabled and there are a million web requests every month.\nThen my price is:\nWAF ACL - $5.00 Rules - 3 * $1.00 = $3.00 Charge for AWS managed rule groups - $0.00 Requests - $0.60 (for million requests) My total WAF cost is $8.60 per month.\nNow I enable Bot Control. Additional costs are:\nBot Control Subscription fee - $10.00 Bot Control Request fee - $1.00 (for million requests) Now my total WAF cost with Bot Control enabled is $19.60 per month.\nThis is a screenshot from Cost Explorer dashboard for AWS WAF.\n3. Trying to tune the Bot Control in the staging/dev environment doesn\u0026rsquo;t work for everyone You will have a hard time finetuning bot control logic if there are a lot of QA checks and automation done on dev/staging environments.\nIn the staging environment, I saw a lot of requests from Postman, Puppeteer (Headless Chrome), curl requests, etc. If I fine-tune the rules to ignore these requests, then these rules would also ignore them in production (which fails the reason we have bot control in the first place).\nAlso, these environments most probably will not have callbacks or webhooks from other servers or clients that are present in prod (if any).\nBecause of these two reasons, finetuning bot control rules in the dev/staging environment didn\u0026rsquo;t work.\nSo, I enabled it on production in \u0026ldquo;Count\u0026rdquo; mode and then finetuned the rules.\n4. Bot Control doesn\u0026rsquo;t detect bot abuses Let\u0026rsquo;s say you were considering bot control to block bot traffic to public endpoints (like login, comment, etc) that can be abused. Then default bot control rules either blocks entire bot traffic or don\u0026rsquo;t block it at all (if it doesn\u0026rsquo;t consider it as bot traffic). You will need custom rules over the labels to make sure you block bot traffic and not genuine traffic.\nThe signal KnownBotDataCenter can help block the requests originating from cloud providers and other bot data centers. But you need to take a call if it\u0026rsquo;s bot/crawler traffic from those IPs or some valid webhook callbacks from them.\n5. It is easy to bypass bot detection To bypass AWS bot detection, all one needs to do is: use a valid User-Agent (of a browser) and send requests from non-cloud provider IPs (say from your mobile internet with a non-static public IP).\nBy this you would bypass any complex bot traffic prevention setup on AWS WAF.\nNOTE: This is based on my observations only. There could also be other unknown factors that AWS use to detect bot traffic which I didn\u0026rsquo;t encounter. The bot control logic might be enhanced by AWS anytime without notice as well.\nConclusion Bot Control is an interesting WAF feature that tries to detect bot traffic. This along with other web ACL features allows the creation of complex rules to handle bot traffic with custom actions (like block, ratelimit, or captcha).\nYou should consider the bot control feature if:\nyou already have AWS WAF enabled on your websites you want simple bot control detection capability and take custom actions you want to block some web bots and crawlers but not all You shouldn\u0026rsquo;t consider the bot control feature if:\nyou want 100% prevention of bot traffic you want a solution to prevent API abuse you are concerned about your existing AWS WAF bill Are you still in a dilemma if you should or shouldn\u0026rsquo;t use Bot Control?\nTry answering this question: Would you like to double your AWS WAF bill for a feature that tries to block lesser than X% of bot traffic reaching your websites (given that the protection can be bypassed)?\nReplace X with whatever percent you see in your production.\nI wish I knew these earlier about the bot control feature.\nPlease feel free to reach out to me on Twitter or LinkedIn if you have any queries.\n","tags":["AWS","WAF","Bot Control"],"title":"Things I wish I knew about AWS WAF - Bot Control"},{"categories":null,"date":"July 10, 2020","permalink":"https://badshah.io/writeup/vulnerable-api/","section":"blog","summary":"Most of the applications I see these days heavily depend on APIs. Pentesting them is a bit different than that of web applications. In this writeup I will show you how I discovered the vulnerabilities in the \u0026ldquo;Vulnerable API\u0026rdquo; project.\nAbout Vulnerable API project According to the documentation, vulnerable API (vAPI) is a set of API endpoints written specifically to illustrate common API vulnerabilities. vAPI is implemented using the Bottle Python Framework and consists of a user database and a token database.\nThis project mimics the real world scenario and is not a blind Capture the Flag type challenge. The project is more of a practice ground to budding pentesters and security consultants. vAPI comes with the following vulnerabilities:\nTransport Layer Security User enumeration Information exposure through server headers Authentication bypass User input validation SQL injection Error handling Session management Encryption AuthN bypass Command Injection Regex DDoS The different API endpoints, requests to them and their responses, user credentials, etc are all documented in the project\u0026rsquo;s README.\nSetup vAPI The setup is quite simple:\nInstall Docker Run the vAPI docker container: docker run -tid -p 8081:8081 --name api mkam/vulnerable-api-demo If the above is successfully setup, you would see the following when you hit http://localhost:8081 on your browser:\nLet the hunt begin Usually if you have an API server and you can\u0026rsquo;t find the endpoints (say from mobile apps, JS files fetched from archive.org, Google dorks, etc) it\u0026rsquo;s almost a dead end. You can be lucky if the API server is in itself vulnerable to some unauthenticated RCE. In this challenge, we have a documented list of API endpoints along with requests and expected responses.\nBefore we check the API endpoints mentioned, let\u0026rsquo;s see if there is any interesting information in the response.\nThe server discloses the header Server: WSGIServer/0.1 Python/2.7.11 in the response. A quick Google search doesn\u0026rsquo;t show any known vulnerabilities for this server version.\nNext let\u0026rsquo;s look at the docs to get the list of endpoints. The endpoints are:\nPOST /tokens : to login GET /user/USER_ID : to get user information (USER_ID is a number) POST /user : to create users GET /uptime and GET /uptime/FLAG : to get uptime (FLAG is s as per the docs) Let\u0026rsquo;s check how the server responds to these endpoints for different HTTP verbs.\nApart from the HTTP method and endpoint combinations mentioned in the docs, we find that GET /tokens is also available. This endpoint discloses all user credentials in the database. This is our first bug.\nNow let\u0026rsquo;s test the endpoints one by one.\nLogin : POST /tokens JSON Body\ncurl -i -s -k -X \u0026#39;POST\u0026#39; \\ -H \u0026#39;Content-Type: application/json\u0026#39; \\ --data-binary \u0026#39;{\u0026#34;auth\u0026#34;: {\u0026#34;passwordCredentials\u0026#34;: {\u0026#34;username\u0026#34;: \u0026#34;user1\u0026#34;, \u0026#34;password\u0026#34;:\u0026#34;pass1\u0026#34;} } }\u0026#39; \\ \u0026#39;http://localhost:8081/tokens\u0026#39; XML Body\ncurl -i -s -k -X \u0026#39;POST\u0026#39; \\ -H $\u0026#39;Content-Type: application/xml\u0026#39; \\ --data-binary \u0026#39;\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt;\u0026lt;auth\u0026gt;\u0026lt;passwordCredentials\u0026gt;\u0026lt;username\u0026gt;user1\u0026lt;/username\u0026gt;\u0026lt;password\u0026gt;pass1\u0026lt;/password\u0026gt;\u0026lt;/passwordCredentials\u0026gt;\u0026lt;/auth\u0026gt;\u0026#39; \\ \u0026#39;http://localhost:8081/tokens\u0026#39; Following observations were made:\nTakes user credentials and provides auth token with an expiry time. The auth token is the same till the expiry time. So if there are two or more login attempts with the same user credentials in a given short interval, you get same auth token. However, expired tokens can be used to fetch data from the endpoints. The API endpoint only responds when the request contains the content type as application/json and application/xml. This means that the endpoint is expecting JSON / XML which contains the user credentials. There is no difference in the response when logged in as a user or admin. Login functionality always calls you to check few bugs: SQLi and rate limiting.\nThis endpoint doesn\u0026rsquo;t have any kind of ratelimit. Adding ' at the end of username gives an error which is likely to be a SQLi.\nWith the help of SQLi, you can login to any user account just by appending '-- at the end. This doesn\u0026rsquo;t require the user\u0026rsquo;s password.\nLogin using XML always calls to look for XXE. Appending DOCTYPE ENTITY responds with \u0026quot;password does not match\u0026quot; instead of an error. This might be because it\u0026rsquo;s ignoring the payload or successfully replacing it.\nI was unable to confirm if it was vulnerable to XXE.\nGet user info : GET /user/USER_ID curl -i -s -k \\ -H \u0026#39;X-Auth-Token: YOUR_AUTH_TOKEN\u0026#39; \\ \u0026#39;http://localhost:8081/user/1\u0026#39; Following observations were made:\nThis endpoint requires X-Auth-Token header with a token to work Works only when the user attempts to access details of his account. Trying to access other account will give the error: the token and user do not match!. If the account doesn\u0026rsquo;t exist, it gives an error user id XX not found where XX is the user ID. Adding a ' at the end of user ID showed it to be vulnerable to SQLi. Running sqlmap produced the following:\nDumping the DB (sqlmap -r req.txt --delay 5 --dump):\nCreate user : POST /user curl -i -s -k -X \u0026#39;POST\u0026#39; \\ -H \u0026#39;X-Auth-Token: YOUR_ADMIN_AUTH_TOKEN\u0026#39; -H $\u0026#39;Content-Type: application/json\u0026#39; \\ --data-binary \u0026#39;{\u0026#34;user\u0026#34;:{\u0026#34;username\u0026#34;: \u0026#34;USERNAME\u0026#34;, \u0026#34;password\u0026#34;: \u0026#34;PASSWORD\u0026#34;}}\u0026#39; \\ \u0026#39;http://localhost:8081/user\u0026#39; Observations:\nAdmin token is required to create users The user is created when username has atleast one character appended with a digit at the end Since this API requires Admin token to create users, let\u0026rsquo;s check if we can bypass this. Adding ' at the end of X-Auth-Token creates an error.\nLooks like a SQL query again. The backend SQL statement verifies both the auth token and the userid. Finally, with the help of SQL injection, one can create users even without admin token using the payload ' OR userid = 10-- Using this anyone can create a user, but still created username should be a string ending with a number. Most probably regex is used in the backend to validate the usernames. If it\u0026rsquo;s regex, we need to give Regex DoS (ReDoS) a try. Passing a long string that doesn\u0026rsquo;t match a regex is a simple test to check ReDoS.\nCreating user aaaaa takes 2 ms.\nCreating user aaaaaaaaaaaaaaaaaaaaaaaaaaa takes 14,927 ms. Also the time taken to create almost doubles each time an alphabet is appended.\nSo the server is vulnerable to ReDoS. Combining both issues (SQLi + ReDoS), any unauthorized user can bring down the server.\nUptime : GET /uptime curl -i -s -k \u0026#39;http://localhost:8081/uptime\u0026#39; curl -i -s -k \u0026#39;http://localhost:8081/uptime/s\u0026#39; Observations:\nIt\u0026rsquo;s an unauthenticated API endpoint This endpoint seems to execute the command uptime along with it\u0026rsquo;s flags (like -s) in the backend The endpoint gives 404 when tried to append / after /uptime/s This is a classic command injection. To exploit this endpoint, append ; or \u0026amp;\u0026amp; after the flag.\nExample:\nThere is an issue when you try to access file paths or execute commands that have / in it. The webserver parses it as URL inspite of URL encoding.\nDouble encoding doesn\u0026rsquo;t work as well. To bypass this, I hosted a static payload file on a webserver and piped it to bash shell. It worked.\nOther issues The connection to vAPI server is in plain HTTP and is obviously vulnerable to MitM attack. Theres no transport layer security. The endpoint GET /user/USER_ID responds with username and password. This means the password is not hashed. Verbose error message is disclosed along with code. ","tags":["API Security","pentest"],"title":"Vulnerable API writeup"},{"categories":null,"date":"June 6, 2020","permalink":"https://badshah.io/blog/remove-domain-takeover-bug-class/","section":"blog","summary":"Dangling DNS records are not something new. They are just out-of-date DNS records which may have served its purpose in the past. This DNS record trash has been there for ages and was not considered a security issue. They are pointing to some resource (IP or DNS record) that was owned/trusted in the past.\nWhat makes the dangling DNS record deadly is the fact that others can seize the resources that the record is pointing to. DNS record takeover, majorly known as domain/subdomain takeover is just exploitation of dangling DNS records. This DNS record takeover falls under security misconfigurations bug class, but I think it has matured enough to make itself a bug class.\nIn this blog post, I will walk you through the (sub)domain takeover bug class, the different types of takeovers, and finally the mitigations. To keep this blog post simple, let\u0026rsquo;s assume that we use AWS services for DNS management and all other workflows. The mitigations that I will show you is cloud provider agnostic. You can use the same methodology even if you use a different cloud provider like GCP, Azure, etc, or a mix of cloud providers.\nWhat is a (sub)domain takeover Domain/subdomain takeover is when an external entity acquires the resource (IP/DNS record) that the dangling DNS record points to. It\u0026rsquo;s technically controlling the resource that DNS record points to, which in turn controls the content behind the DNS record.\nTypes of takeovers There are many types of DNS records, however, 4 of them can be affected because of this bug class. They are:\nCNAME record A record MX record NS record Exploiting these dangling DNS record types form the basic attack vectors for (sub)domain takeover bug class. Each attack vector might look similar but the impact and probability to exploit differs.\nCNAME takeover This is the most commonly exploited attack vector. There are multiple open-source tools to detect and takeover the subdomains if they are found to be vulnerable. There\u0026rsquo;s a lot of competition among bug hunters to find this kind. This is because:\nEasy to judge if a CNAME record is vulnerable (because of some identifiers). In most cases it\u0026rsquo;s just an error message that discloses if the subdomain is vulnerable. Most bug bounty programs consider this as Medium to High severity bug. So it\u0026rsquo;s a bug that assures bounty. Detection and exploitation can be completely automated. Lots of open-source tools that readily available. Easy bounty if found. This bug is not just a mistake on the company side, the SaaS providers are also part of the blame. Lots of SaaS vendors don\u0026rsquo;t have proper verifications/controls in place which leads another user to acquire the same resource that the company / other users had in the past. The CNAME takeovers will continue to exist till all the SaaS providers have proper verifications. The common reasons for this attack vector are:\nThe company no longer uses the SaaS product and the SaaS provider allows the reuse of the same subdomain Record pointing to expired domains Typo in resource name the record points to A takeover This is when the A record points to IP address(es) which is no longer owned by the company. The A record takeover is more common than other record type takeovers, mainly because of how the cloud providers assign public IPv4 addresses to VMs. However, this attack vector is rarely exploited and is done only by a few bug hunters and security researchers. Reasons for this being a rarely exploited are:\nIt\u0026rsquo;s hard to determine if a particular IP pointed by the A record is vulnerable and exploit it. There\u0026rsquo;s no clear identifier that tells if a domain is vulnerable like in the case of CNAME takeover. If an IP doesn\u0026rsquo;t have open ports, it can be\nbehind a firewall reassigned to some other cloud user and can\u0026rsquo;t be exploited until they release it released to the cloud providers IP pool The exploitation is likely possible when a subdomain points to a single IPv4 address which was released to the IP pool and is assigned to the attacker by the cloud provider.\nThere is less chance of success when targeting a single organization/bug bounty program.\nThis attack vector also depends on the cloud provider. This takeover is possible if the cloud provider randomly assigns you a public IP each time you create a VM. If the cloud provider has some algorithm that assigns you an IP from a small set of IPs instead of the entire IP pool, then exploitability becomes hard.\nMX takeover This method is similar to the CNAME takeover attack vector. The vulnerable scenarios remain the same as those in the CNAME takeover. But MX takeover has a comparatively low impact. This is because claiming the resource in MX record can only allow you to receive emails.\nAnother unpleasant fact is that there\u0026rsquo;s usually more than 1 MX record for a domain. If only 1 among 3 MX records is available for takeover, then the impact depends on the priority of vulnerable MX records. Successful exploit of the MX record might almost lead to phishing attacks (with reply to email as one of the valid subdomains) and intellectual property stealing.\nNS takeover This method is also similar to the CNAME takeover attack vector. The impact of this is comparatively high as the takeover of the name server address allows you to become an authoritative name server of the domain. But like MX records, there is usually more than one NS record for a registered domain.\nNS record errors are the rarest because:\nMost domains have name servers that are configured to all respond all queries. Usually they are managed name servers of the domain registrars / cloud providers. It\u0026rsquo;s only few companies that have separate NS records for it\u0026rsquo;s subdomains. This vector tend to occur only when there are two or more NS records. If there\u0026rsquo;s a single NS record configured and its not properly setup, the domain resolution would not work in the first place. This is also the reason why successful takeover of NS might not affect 100% of the users trying to connect to the site. The most common reasons for this attack vector are:\nExpiry of the domain mentioned in NS record Typo in NS records To know more about this takeovers look at the resources. Don\u0026rsquo;t forget to check Patrik Hudak\u0026rsquo;s blog posts which beautifully describes these types of takeovers and its impact.\nWith the research and my experience of subdomain takeover detection, I can say the following:\nType Occurrence Effort to Detect \u0026amp; Exploit Impact A takeover MEDIUM HIGH HIGH CNAME takeover MEDIUM LOW HIGH MX takeover MEDIUM LOW LOW NS takeover LOW LOW HIGH Impact When an attacker gets hold of it, he/she can do multiple things. According to a Medium blog post, the impact includes:\nThe ability to host malicious files that can be used to steal user data. The ability to launch phishing campaigns on behalf of the targeted company. The ability to chain other vulnerabilities to take over user accounts and other sensitive information The ability to display malicious content that would degrade the usability and accountability of that company. Other impacts include:\nAbility to register to services where the verification of a domain is text / HTML file upload. Example: Issuing an SSL certificate based on HTML file upload challenge Mitigation The mitigation for this bug class cannot be achieved only using technology. You need to add people and processes for this mitigation formula to work.\nPeople and Process You can imagine writing an amazing tool to get all registered domains of your company, their DNS records and make sure it\u0026rsquo;s not vulnerable to takeover. However, in reality, it\u0026rsquo;s not easy as writing code. If there\u0026rsquo;s no guidelines/process which tells employees where to register domains for the company and how to manage DNS records, they are going to do it whichever they find easy (more common in startups). This means that your amazing tool secures a part of the overall domains owned by the company.\n\u0026ldquo;You can\u0026rsquo;t protect what you can\u0026rsquo;t see\u0026rdquo;\nHave a centralized DNS management which includes both domain registration and DNS records management. If that\u0026rsquo;s not possible / already using multiple providers, then have a strict process to limit the number of providers. By doing this you will not come across a subdomain takeover bug report of a subdomain which you never knew existed.\nTechnology Once the process is set then you can solve the rest of this problem with automated audits. The audit on a high level will look like the following:\nGet the list of all public IPs you own\nIf you are using AWS, almost all public IPs are found in AWS EC2 service (Public Instance IPs, Elastic IPs, NAT Gateways, Elastic Network Interfaces). Create a script to dynamically get the public IPs.\nGet the list of Cloud Provider services which creates custom subdomains\nUnderstand what cloud provider services are being used in the company. If the services create a custom subdomain, update the script to dynamically fetch the custom subdomains. The list of AWS services that create custom subdomains includes AWS Cloudfront, AWS S3, AWS ELB, etc.\nGet a list of DNS records that you own\nFetch the list of all DNS records that can be affected by this bug class, namely A, CNAME, MX and NS records. In AWS, you get it from Route53. Here are some tips that helps you to write the logic of the script faster:\nOnly collect the A records which point to public IPs. A records pointing to private IPs cannot be taken over. In the case of CNAME, MX and NS records its hard to determine if the pointed resource is private or public. So note it all. Theres no problem in case the domain/subdomain pointed by a DNS record is private and hosted in our AWS zones. If any DNS records are not pointing to IPs / DNS records / Cloud Provider services that you own, flag it\nCross check each DNS record with the IPs and other subdomains you own. In case of A records, check if the public IP is in the list of IPs you own. For other DNS record types, check if it is pointing to a DNS record that you own. If the DNS record doesn\u0026rsquo;t match a resource that we own, then it means it is pointing to an external resource.\nCheck the flagged DNS records if they are whitelisted (known 3rd party domains and IPs). If not, then report it\nFor each DNS record that doesn\u0026rsquo;t point to a resource owned by the company, check if they are whitelisted (known DNS records). If not, then report it. If the DNS record was intentional, then add it to the whitelist else delete the DNS record.\nYou can further increase the effectiveness of detection by the following:\nFor CNAME records, you can run available takeover detection tools to find if they are vulnerable. Whitelisted 3rd party resources are not permanent, it needs to be regularly checked but not frequently. For the whitelisted domains, have a periodic check to make sure that the 3rd party resource was not expired/abandoned by the teams. How NOT to solve this problem While researching the solution for this (sub)domain takeover bug class, I found other alternate solutions that aren\u0026rsquo;t effective. Here are some ways on how you SHOULD NOT solve this problem:\nHiring companies to report/mitigate subdomain takeovers\nThis is what I call \u0026ldquo;Cutting it from the outside in\u0026rdquo; approach. Any external bug hunter/security company can only gather the subdomains from different sources, like Shodan, Censys, VirusTotal, etc. However, this list is not 100%. After analyzing these subdomains, there is a possibility of still having subdomains leftover that can be vulnerable to takeover.\nDNS management services like AWS Route53 are a single source of truth for all DNS records owned by the company. Do not follow this method unless your company does not allow you to access the DNS management service.\nTrying to use static IPs for all the public instances and deleting DNS records before releasing the IP\nOne idea to remediate A record takeovers would be to use static IPs for all VMs on the cloud. Then before releasing it, check for all the A records pointing to the IP and remove them. By doing this, we will remediate instant dangling DNS records and will get time till we remove the DNS record. This might sound promising at first (in theory). However, in reality, the number of static IPs that could be assigned is dependent on the cloud provider.\nIn AWS, you can only have 5 Elastic IPs per region. Also, to make things worse, the Elastic IPs are charged separately if they are not assigned to any VMs. Even using Elastic IPs, if you release it without deleting DNS records, it remains vulnerable. So, this is a bad solution and can\u0026rsquo;t be used.\nResources CCS 2016 - All Your DNS Records Point to Us Fishing the AWS IP Pool for Dangling Domains Dangling DNS Records are a Real Vulnerability Domain Security \u0026amp; Solutions, Part 3: Stale DNS Records \u0026amp; Subdomain Takeover Attacks Subdomain Takeover: Basics Subdomain Takeover: Thoughts on Risks ","tags":["Subdomain Takeover","DNS","AWS"],"title":"How to remove DNS record takeover bug class ?"},{"categories":["experiments"],"date":"March 22, 2020","permalink":"https://badshah.io/experiment/adding-gitleaks-to-gitlab-pipeline/","section":"blog","summary":"Gitleaks has become quite popular. Its features gives a tough competition to its predecessor trufflehog. Some of its uber cool features are:\nComparatively fast when scanning large repos (as it is a compiled Golang binary) It can run on all platforms that Golang supports. User can add custom regex to detect more secrets Allows whitelisting of detected secrets / false positives Allows audit of GitLab and GitHub repos, groups and orgs. There are an article on how to add Gitleaks to Gitlab pipeline by Cyber Defence Lab. But the CI pipeline is not quite optimised. In this experiment, I will show you how to implement gitleaks to GitLab CI pipeline.\nSetting up gitleaks on Gitlab CI Gitleaks is available as docker image. We can directly download use them on our Gitlab CI config. Gitleaks scans for all commits. It can be optimised to scan the new commits that were recently pushed to a particular branch.\nstages: - secrets-detection gitleaks: stage: secrets-detection image: name: \u0026#34;zricethezav/gitleaks\u0026#34; entrypoint: [\u0026#34;\u0026#34;] script: gitleaks -v --pretty --repo-path . --commit-from=$CI_COMMIT_SHA --commit-to=$CI_COMMIT_BEFORE_SHA --branch=$CI_COMMIT_BRANCH ","tags":["Gitleaks","GitLab","Secrets Detection"],"title":"Adding Gitleaks to Gitlab CI Pipeline"},{"categories":["experiments"],"date":"February 23, 2020","permalink":"https://badshah.io/experiment/cloud-function-to-pub-sub/","section":"blog","summary":"Every time I want a cloud managed message queue, I would look at AWS SQS service. It\u0026rsquo;s simple. Create a SQS queue, get the HTTP endpoint for the queue, start posting the messages using any HTTP client like curl.\nThis time I made up my mind to give GCP\u0026rsquo;s counterpart a try.\nThe GCP\u0026rsquo;s counterpart is Pub/Sub. On a high level, the queue is known as topic in the Pub/Sub terms. There will be one or more publishers who will publish to the topic and one or more consumers who will subscribe to the topics. There are other differences with GCP\u0026rsquo;s Pub/Sub and AWS\u0026rsquo; SQS but thats not relevant to this experiment.\nGCP allows different ways to publish messages to a topic but not simple HTTP endpoint. Searching online on how to add a http endpoint to the Pub Sub gave multiple (useless) answers. Since GCP documentation has used very good SEO techniques, you tend to NOT get the answers for any GCP related queries easily.\nFinally, I found that Cloud Functions is the simplest way to attach a HTTP endpoint. Here’s how I did it:\nCreate a GCP Service Account and assign it the role \u0026ldquo;Pub/Sub Publisher\u0026rdquo;. This will be a Google managed service account. In Pub/Sub section, create a topic with Encryption set to \u0026ldquo;Google-managed key\u0026rdquo; In Cloud Function section, click on \u0026ldquo;Create function\u0026rdquo;. For this example I would be using Python runtime with the following code: import os from google.cloud import pubsub_v1 def hello_world(request): try: testAttribute = request.get_json().get(\u0026#39;test\u0026#39;) topic_name = \u0026#39;projects/badshah-project/topics/pubsub-publisher\u0026#39;.format( project_id=os.getenv(\u0026#39;GOOGLE_CLOUD_PROJECT\u0026#39;), topic=\u0026#39;pubsub-publisher\u0026#39;, ) publisher.publish(topic_name, b\u0026#39;\u0026#39;, testAttribute=testAttribute) return f\u0026#34;OK\u0026#34; except: return f\u0026#34;Not OK\u0026#34; Make sure you add google-cloud-pubsub to the requirements.txt section\nClick on \u0026ldquo;Environment variables, networking, timeouts and more\u0026rdquo;. Under \u0026ldquo;Service account\u0026rdquo; select the service account that was created in step 1.\nTo trigger this cloud function, use the following curl command:\ncurl -X POST https://asia-east2-badshah-project.cloudfunctions.net/pubsub-publisher \\ -H \u0026#34;Content-Type:application/json\u0026#34; \\ -d \u0026#39;{\u0026#34;test\u0026#34;:\u0026#34;Hello World\u0026#34;}\u0026#39; When this returns a message \u0026ldquo;OK\u0026rdquo;, then an empty message with attribute test = Hello World is published into the PubSub topic.\n","tags":["Cloud","Cloud Function","GCP","PubSub","Serverless"],"title":"Creating a Cloud Function to publish messages to Pub/Sub"},{"categories":["experiments"],"date":"February 17, 2020","permalink":"https://badshah.io/experiment/fast-reverse-dns-lookups-using-fdns-and-mongodb/","section":"blog","summary":"As part of my research, I wanted a way to find all the DNS records which points to a particular IP address. Not only should it be fast, it should be cheap as well. If you are a DNS researcher you would know about the Rapid7’s free FDNS dataset.\nI was not able to find any online post that showed me how to get subdomains using IP address in the FDNS dataset. There were plenty of articles that said how to find all the subdomains using the domain name.\nThere are some cloud solutions like AWS Athena and GCP BigQuery that makes the life easier when dealing with large datasets. The life seems easy till the monthly bill of the cloud service arrives in your email inbox.\nFinally I wanted to give databases a shot and selected MongoDB for its simplicity when loading JSON documents to the DB.\nLoading FDNS dataset to MongoDB I setup a mongo DB instance on my local machine. Then I tried to import the uncompressed JSON data to a database called fdns and collection named dns.\npigz -dc fdns.json.gz | mongoimport --db fdns --collection dns After the data is loaded into database, one could go ahead and search for all domains pointing to an IP using the following command.\nuse fdns db.dns.find({\u0026#34;value\u0026#34;: \u0026#34;127.0.0.1\u0026#34;}) The problem with this is it searches all the rows to check if the IP is 127.0.0.1. If we have 100k JSON documents (rows) in the collection, it searches for all the 100k JSON documents and returns us the documents whose value matches 127.0.0.1. This takes a lot of time. If you want to do it just once, this is the most simplest method.\nCreating MongoDB Indexes If you want to reverse lookup multiple times then you will definitely be frustrated with the time it takes to complete one search. To save everyone from this frustration, MongoDB has something called Indexes. Creating index will reduce the search time to seconds.\nAs we search the value field (column in MongoDB terms) and try to get the subdomain, to make our search faster we need to create the index based on the value field.\nuse fdns db.dns.createIndex( { value : 1 } ) This creation of index takes long time but once it is done, the search just takes seconds to complete.\n","tags":null,"title":"Fast Reverse DNS Lookups using FDNS and MongoDB"},{"categories":["experiments"],"date":"February 17, 2020","permalink":"https://badshah.io/experiment/faster-nmap-scanning-with-the-help-of-gnu-parallel/","section":"blog","summary":"When you give access to developers to create firewall rules, they generally open all sorts of ports to the internet. All they need to do / expected to do is get the product working. The rest doesn’t matter.\nThis was the same with the company I worked with. There were tonnes of firewall rules in the GCP projects which opened many ports to the public internet. One could think of deleting firewall rules straight away and check if something is breaking on production. But that’s a bad solution.\nSecurity measures, in my opinion, should not be at the cost of production downtime (*unless there is a very critical bug to patch on prod).\nSo the next idea was to scan all the public IPs of VMs and K8s nodes and get a list of closed ports. This will help us get a list of firewall rules that are no longer required.\nGot a CSV with GCP project name and IP address assigned with the project. The CSV file looks something like the below (headers stripped off):\nprojectOne,xxx.xxx.xxx.xxx projectOne,xxy.xxx.xxx.xxx projectTwo,yyy.yyy.xyx.xxx After looking at the time it took to complete nmap scan on one single IP for all ports, I was sure that running scan for 1000+ IPs would take a very long time.\nGNU Parallel for the win ! GNU Parallel is a command line tool that allows to execute multiple jobs in parallel. A job can be a single command or a small script that has to be run for each of the lines in the input.\nFor my case, I want to run nmap scan in parallel.\nmkdir logs cat gcp-project-ips.csv | cut -f1 -d, | sort -u | while read project; do mkdir logs/${project}; done cat gcp-project-ips.csv | while IFS=\u0026#34;,\u0026#34; read -r project ip; do echo nmap -sT -T5 -Pn -p- -oG logs/${project}/${ip}.gnmap $ip; done \u0026gt; scan-all-ips.out parallel --jobs 32 \u0026lt; scan-all-ips.out The above code starts 32 parallel nmap scans. You can tune the number of jobs as per your network bandwidth and the CPU limit. The greppable output of the nmap scan is put into logs/{project}/{ip}.gnmap files once the scan is done.\nThe other part of the job was to grep the closed ports from the nmap output files and report it in a parsable format.\nfor dirname in logs/*; do for filename in ${dirname}/*.gnmap; do project=`echo $filename | cut -d/ -f2`; ip=`echo $filename | cut -d/ -f3`; ip=${ip::-6}; cat $filename | cut -d\u0026#39; \u0026#39; -f2,4- | sed -n -e \u0026#39;s/Ignored.*//p\u0026#39; | awk -v ip=\u0026#34;$ip\u0026#34; -v project=\u0026#34;$project\u0026#34; \u0026#39;{$1=\u0026#34;\u0026#34;; for(i=2; i\u0026lt;=NF; i++) { a=a\u0026#34; \u0026#34;$i; }; split(a,s,\u0026#34;,\u0026#34;); for(e in s) { split(s[e],v,\u0026#34;/\u0026#34;); if (v[2] == \u0026#34;closed\u0026#34;) { printf project \u0026#34;,\u0026#34; ip \u0026#34;,%s,%s\\n\u0026#34; , v[1], v[5]}}; a=\u0026#34;\u0026#34; }\u0026#39;; done; done ","tags":["Linux","nmap"],"title":"Faster nmap scanning with the help of GNU parallel"},{"categories":null,"date":"November 27, 2019","permalink":"https://badshah.io/blog/finding-route53-logs-with-the-help-of-cloudtrail-and-athena/","section":"blog","summary":"If your company uses AWS Route53 to manage DNS records of its domains, there might a situation where you want to find which IAM user created / modified which DNS record.\nThe reason for this could be anything: asset management, to find root cause of a security incident (like subdomain takeover) or simply to cleanup unused DNS records.\nThis could be done with ease if you have CloudTrail logging enabled (atleast for us-east-1 region) and stored on an S3 bucket. If you don’t have it enabled during the time of DNS management, this blog post will not be of much help to you.\nBefore we move on, let’s know what is AWS Athena.\nWhat is AWS Athena ? Athena is a query service by AWS, which allows you to query data stored in S3 bucket using standard SQL. You can read more about AWS Athena here.\nCloudtrail logs all the API calls made to AWS services and stores them on S3 buckets. We can utilize Athena service to query Cloudtrail logs to get required Route53 logs.\nCreating Athena table from CloudTrail logs Go to CloudTrail dashboard, click on \u0026ldquo;Event History\u0026rdquo; and then click \u0026ldquo;Run advanced queries in Amazon Athena\u0026rdquo;\nSelect the S3 bucket which is used to store the CloudTrail logs. Click on \u0026ldquo;Create Table\u0026rdquo;.\nBy default, the name of the S3 bucket which stores the Cloudtrail logs is appended to the Athena table name. For example, if my S3 bucket name is badshah, then my default Athena table name is cloudtrail_logs_badshah. You can change the name of the table before it’s creation (it can’t be changed after creation).\nBefore executing the queries, please remember:\nI have used cloudtrail_logs_badshah as my table name for all the following queries. Replace it with your table name. The query output includes only username, account ID, creation date, domain name, record type and the resource records. If you want to get all columns, you can use SELECT * FROM cloudtrail_logs_badshah. Athena query to get Route53 logs for DNS record creation SELECT useridentity.username, useridentity.accountid, useridentity.sessioncontext.attributes.creationdate, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.name\u0026#39;) AS domainName, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.type\u0026#39;) AS recordType, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.resourceRecords\u0026#39;) AS resourceRecords FROM cloudtrail_logs_badshah WHERE (eventsource = \u0026#39;route53.amazonaws.com\u0026#39; AND eventname = \u0026#39;ChangeResourceRecordSets\u0026#39; AND json_extract_scalar(requestparameters,\u0026#39;$.changeBatch.changes[0].action\u0026#39;) = \u0026#39;CREATE\u0026#39;) A successful execution will give output like the below:\nAthena query to get all Route53 logs If you want to get all the actions of DNS records, then use the following:\nSELECT useridentity.username, useridentity.accountid, useridentity.sessioncontext.attributes.creationdate, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].action\u0026#39;) AS action, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.name\u0026#39;) AS domainName, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.type\u0026#39;) AS recordType, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.resourceRecords\u0026#39;) AS resourceRecords FROM cloudtrail_logs_badshah WHERE (eventsource = \u0026#39;route53.amazonaws.com\u0026#39; AND eventname = \u0026#39;ChangeResourceRecordSets\u0026#39;) A successful execution will give output like the below:\nAthena query to get all Route53 logs for a particular DNS record Make sure you change the Athena table name cloudtrail_logs_badshah and the DNS record sub.your-domain.com.. Don’t forget the . at the end of the DNS record.\nSELECT useridentity.username, useridentity.accountid, useridentity.sessioncontext.attributes.creationdate, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.name\u0026#39;) AS domainName, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.type\u0026#39;) AS recordType, json_extract(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.resourceRecords\u0026#39;) AS resourceRecords FROM cloudtrail_logs_badshah WHERE (eventsource = \u0026#39;route53.amazonaws.com\u0026#39; AND eventname = \u0026#39;ChangeResourceRecordSets\u0026#39; AND json_extract_scalar(requestparameters,\u0026#39;$.changeBatch.changes[0].resourceRecordSet.name\u0026#39;) = \u0026#39;sub.your-domain.com.\u0026#39;) A successful execution will give output like the below:\n","tags":["AWS Athena","AWS","Cloud","Cloudtrail","DNS","Route53"],"title":"Finding Route53 logs with the help of CloudTrail and Athena"},{"categories":null,"date":"September 12, 2019","permalink":"https://badshah.io/blog/backup-and-restore-elasticsearch-cluster-using-gcs/","section":"blog","summary":"You don’t know what you got until it’s gone. And unfortunately it’s the same with data.\nThe importance of backup is mostly realised after loosing the data. After a few data incidents, I have made backups of all my ElasticSearch clusters. I have also made it a habit to create backups and then go ahead with using ES cluster to store data.\nIn this article I will be using Google Cloud Storage (GCS) buckets for backup. If you are looking to backup ElasticSearch data on AWS S3 instead of Google GCS, then this Medium article will help you.\nRequirements For this article, you would require the following:\nElasticSearch instance which we are interested to take backup GCP account which allows you to create service accounts, add IAM roles and create GCS buckets. Backup ElasticSearch data to GCS Create a GCS bucket. Let’s call it backup_bucket.\nCreate a GCP service account with storage.objectAdmin permission. As it will be a user managed service account, create a JSON private key and download it. Let’s say the file to be service-account-es-backup.json.\nAdd the service account credentials to ElasticSearch KeyStore. The binary for this is usually at the location /usr/share/elasticsearch/bin. If not then locate it on the ES installation directory,\n./elasticsearch-keystore add-file gcs.client.default.credentials_file service-account-es-backup.json After successfully adding the key, you would no longer need the JSON file. Feel free to delete the file from the system.\nInstall GCS backup plugin\n./elasticsearch-plugin install repository-gcs Before creating your first backup, older versions of ElasticSearch would require a restart.\nCreate a backup repository. In ES terminology, a backup repository is a collection of all the snapshots of your backup.\ncurl -X PUT -H \u0026#34;Content-Type: application/json\u0026#34; -d \u0026#39;{ \u0026#34;type\u0026#34;: \u0026#34;gcs\u0026#34;, \u0026#34;settings\u0026#34;: { \u0026#34;bucket\u0026#34;: \u0026#34;backup_bucket\u0026#34;, \u0026#34;base_path\u0026#34;: \u0026#34;es_backup\u0026#34; } }\u0026#39; localhost:9200/_snapshot/backup In the above command, we are creating a backup repository called backup and store the snapshots on a GCS bucket called backup_bucket inside the directory es_backup.\nYou might think that we made our first backup of the data, but we haven’t. We have just created a backup repository to hold snapshots (if any) in future.\nTaking your first backup:\ncurl -X PUT \u0026#34;localhost:9200/_snapshot/backup/first_snapshot\u0026#34; In order to have a regular backups, use an identifiable snapshot name. In my case, I append date with the snapshot.\ncurl -X PUT \u0026#34;localhost:9200/_snapshot/backup/snapshot_`date +\u0026#39;%Y_%m_%d\u0026#39;`\u0026#34; If you are using a cron job to trigger snapshot process, don’t forget to add slash () before %. If not, the cron command would fail.\ncurl -X PUT \u0026#34;localhost:9200/_snapshot/backup/snapshot_`date +\u0026#39;\\%Y_\\%m_\\%d\u0026#39;`\u0026#34; Note: A good rule of thumb for backup would be to use individual buckets for backups of different ES clusters (or any other data). It should not be the situation where you put all your backup eggs in one bucket and sometime in the future you loose the bucket. 🤦‍♂️\nRestore ElasticSearch data from GCS Taking a data backup without knowing how to restore it is dangerous. In some cases, it might be as bad as not taking backups.\nThere can be two situations for restoration process:\nSome data was deleted in the existing cluster and you want to restore it to the previous version. The entire cluster is inaccessible (due to disk corruption / any other strange reasons) and you want to restore the entire backup data to a brand new cluster. Case 1: Restoring to the same cluster As the backup is already set up, you could directly restore using the following command.\ncurl -X POST \u0026#39;localhost:9200/_snapshot/backup/first_snapshot/_restore\u0026#39; If you have multiple snapshots, then select the relevant snapshot ID from the snapshot list.\ncurl \u0026#39;localhost:9200/_cat/snapshots/backup?v\u0026amp;s=id\u0026#39; Case 2: Restoring to a brand new cluster Install the same version of ElasticSearch (to be safe from data incompatibility issues). Install the GCS backup plugin.\nI would show you how to restore the data on a ES cluster running on Docker. If you want to restore on ES cluster outside Docker containers, you could directly execute the commands which are run inside the container.\ndocker pull elasticsearch:VERSION docker network create restore-network docker run -d --name elasticsearch --net restore-network -p 9200:9200 -p 9300:9300 -e \u0026#34;discovery.type=single-node\u0026#34; elasticsearch:VERSION docker exec -it elasticsearch /bin/bash # inside the docker container cd /usr/share/elasticsearch/bin ./elasticsearch-plugin install repository-gcs Create a Google service account that has read only access to the backup bucket. Copy it to the container (or write the contents to a file using nano / vi editor). Let’s call the file service-account-es-restore.json. Add it to the ElasticSearch like we did in the backup process.\n./elasticsearch-keystore add-file gcs.client.default.credentials_file service-account-es-restore.json You might need to restart the container in case of older ElasticSearch version.\ndocker restart elasticsearch Create a backup repository with the same config (bucket name and base path) as before.\ncurl -X PUT -H \u0026#34;Content-Type: application/json\u0026#34; -d \u0026#39;{ \u0026#34;type\u0026#34;: \u0026#34;gcs\u0026#34;, \u0026#34;settings\u0026#34;: { \u0026#34;bucket\u0026#34;: \u0026#34;backup_bucket\u0026#34;, \u0026#34;base_path\u0026#34;: \u0026#34;es_backup\u0026#34; } }\u0026#39; localhost:9200/_snapshot/backup List all the snapshots in the backup repository (which is connected to our GCS backup_bucket).\ncurl \u0026#39;localhost:9200/_cat/snapshots/backup?v\u0026amp;s=id\u0026#39; Select the latest snapshot ID and restore it.\ncurl -X POST \u0026#39;localhost:9200/_snapshot/backup/first_snapshot/_restore\u0026#39; You now would have successfully restored data from your backup.\n","tags":["backup","cloud","ElasticSearch","GCP"],"title":"Backup and restore ElasticSearch data using GCS"},{"categories":null,"date":"June 7, 2019","permalink":"https://badshah.io/blog/how-i-hosted-a-dns-server-on-aws/","section":"blog","summary":"Wait. I know what you are thinking now.\nWho on earth would do such a crazy thing ?\nWhy would a person even host a DNS server on AWS when one could use Route53 to efficiently manage DNS records.\nThe answer is simple:\nI’ve been a user of DNS since my first interaction with the internet, but have no clear idea on how DNS works.\nSo I started learning how DNS works and more on how DNS server works. You can check out my notes on DNS. I felt that the best way to learn about DNS is by practically hosting a DNS server.\nTrust me this way is far better than learning DNS by using a third party DNS service or reading theoretical blog posts on how DNS works.\nI had a few credits left on AWS, that’s when I asked myself this question: “Is it possible to host a DNS server on AWS?”\nCreating the Name Server on AWS The steps are pretty simple:\nCreate an EC2 instance. Free tier version of Ubuntu works fine. (Please read this article to learn how to create EC2 instance). Download the SSH key to access the instance. Create an elastic IP and associate it with the EC2 instance created in the first step. (If you have never worked with elastic IPs, you can read this article) Now the public IP of the EC2 instance will be replaced by the elastic IP. Create a Security Group which allows 0.0.0.0/0 access to port 53 (TCP and UDP) and add this security group to the EC2 instance. This allows DNS interactions from public internet. Setting up the domain name I didn’t really want to spend money for this experiment. So ended up registering a free domain – machane.ga – for one month at FreeNom.\nDid you know ? Machane is the malayalam slang word meaning “dude”\nFreeNom allows user managed DNS name servers. One could create Glue records at the beginning of the registration. I entered two nameservers as ns1.machane.ga and ns2.machane.ga and gave the same AWS elastic IP in both the IP address fields.\nIf you observe above, you can see that I am in the process of registering machane.ga and provided the nameservers as ns1.machane.ga and ns2.machane.ga. That’s the trick in glue records. So, basically we are saying the domain registrar that I am going to register machane.ga and I am also going to host the nameservers for it under the DNS paths ns1.machane.ga and ns2.machane.ga.\nOnce the registration of the domain is successful and logged in to the FreeNom domain nameserver administration panel, one should be able to see something similar to the following:\nSetting up the DNS server I have done the first half of the job. The domain registration is instant but the propagation of the domain’s DNS would take some time (upto 72 hours on FreeNom). In the mean time I could setup the DNS server.\nAfter much fiddling with the installation and configuration of BIND9 DNS server on Ubuntu, I finally created a bash script to automate the setup of DNS zones using BIND server.\nIf you are interested in setting up a DNS server, please make changes (domain name, subdomains, elastic IP, etc) to the following script. Also don’t forget to run it with sudo permission.\napt install bind9 bind9-doc -y echo \u0026#39;include \u0026#34;/etc/bind/named.conf.log\u0026#34;;\u0026#39; | tee -a /etc/bind/named.conf # Store all the logs to /var/log/named/bind.log tee /etc/bind/named.conf.log \u0026lt;\u0026lt; EOF logging { channel bind_log { file \u0026#34;/var/log/named/bind.log\u0026#34; versions 3 size 5m; severity info; print-category yes; print-severity yes; print-time yes; }; category default { bind_log; }; category update { bind_log; }; category update-security { bind_log; }; category security { bind_log; }; category queries { bind_log; }; category lame-servers { null; }; }; EOF # Create the directory for storing logs mkdir /var/log/named chown bind:root /var/log/named chmod 775 /var/log/named/ service bind9 restart # Setup log rotation tee /etc/logrotate.d/bind \u0026lt;\u0026lt; EOF /var/log/named/bind.log { rotate 90 daily dateext dateformat _%Y-%m-%d missingok create 644 bind bind delaycompress compress notifempty postrotate /bin/systemctl reload bind9 endscript } EOF # Change the zone to your domain name tee /etc/bind/named.conf.local \u0026lt;\u0026lt; EOF zone \u0026#34;machane.ga\u0026#34; { type master; file \u0026#34;/etc/bind/zones/db.machane.ga\u0026#34;; }; EOF mkdir /etc/bind/zones # Creating the zone files # Setting AWS elastic IP ElasticIP=xx.xx.xx.xx # Please dont forget . at the end of the DNS entries tee /etc/bind/zones/db.machane.ga \u0026lt;\u0026lt; EOF \\$TTL 900 @ IN SOA ns1.machane.ga. admin.machane.ga. ( 1 ;\u0026lt;serial-number\u0026gt; 900 ;\u0026lt;time-to-refresh\u0026gt; 900 ;\u0026lt;time-to-retry\u0026gt; 604800 ;\u0026lt;time-to-expire\u0026gt; 900) ;\u0026lt;minimum-TTL\u0026gt; ;List Nameservers IN NS ns1.machane.ga. IN NS ns2.machane.ga. ;Create A record IN A 127.0.0.1 ;address to name mapping ns1 IN A $ElasticIP ns2 IN A $ElasticIP ;wildcard DNS entry * IN A 127.0.0.1 EOF service bind9 restart In the above script, I have explicitly mentioned that machane.ga and *.machane.ga (except the subdomains ns1 and ns2) resolve to 127.0.0.1. Once the registered domain has propagated its DNS changes around the world, anyone could query the domain and its subdomains.\nFinal thoughts I have finally created an authoritative DNS server which resolves any subdomain of machane.ga. It will not resolve DNS of any other domain.\nI did this to experiment and learn more about how DNS works (in real life) and DNS servers. I gained a good experience and had a lot of fun along the learning process.\nUntil my next experiment, hasta la vista.\nDISCLAIMER: I have registered the domain for experimental purpose for a single month. Any changes done to the domain’s DNS after a month (from the time of writing this article) will not be me. I am not be responsible for any damage done by reusing the above bash script.\n","tags":["AWS","DNS","AWS Route53","BIND 9"],"title":"How I hosted a DNS server on AWS ?"},{"categories":null,"date":"April 1, 2019","permalink":"https://badshah.io/blog/efficient-way-to-pentest-android-chat-applications/","section":"blog","summary":"Setting up a pentest environment for a single Android application to test its functionalities is simple. The process involves just setting up an Android emulator, installing the app, sending the traffic through a proxy tool like BurpSuite and playing with the traffic to find interesting behaviour.\nWhen it comes to setting up pentest environment for an Android chat application, the setup slightly differs. This is not the case only for chat apps but also for other apps whose functionality (like multi-user authorization) could be completely understood only when running the app in two or more devices simultaneously. In this article, we’ll look at how to setup the Android chat app pentest environment using Genymotion emulator and BurpSuite.\nAssumptions Before we continue with the article I assume that:\nYou are trying to pentest the HTTP calls made by the app and are not interested in the implementation of the app (like Javascript enabled in WebViews, etc) The app doesn’t have any SSL pinning or root detection. Even if the app has, it is assumed that you have bypassed it successfully. The app supports x86 architecture based hardware as Genymotion emulates an x86 Android device. If the app only supports ARM, please use Android Virtual Device (AVD) instead of Genymotion. Please note that running apps on AVD might be comparatively slow. Two or more simultaneously running Android emulators are required for understanding and testing the functionality of the app. You were able to successfully setup Genymotion and BurpSuite Community Edition. Last but not the least, you have permission to test the Android app. Setup the test environment Open Genymotion and download two device types. You can also feel free to download one device and create another clone of it.\nAfter setting up the emulator devices, start them, drag and drop the apk file into the emulator windows to install the app. Fire up BurpSuite Community Edition. Goto Proxy → Options → Proxy Listeners and add two proxy listeners for the host’s IP address which is connected to the emulator, one on port 8080 and another on port 8081. You will understand why we added two ports if you manage to complete this article.\n(Note: If you have installed VirtualBox \u0026amp; Genymotion and haven’t tweaked VirtualBox’s network settings, by default the Android emulator starts with 192.168.56.xxx IP address and your host is assigned with the IP address 192.168.56.1).\nStart an emulator device, goto Settings → Wifi → (Long press Wifi network) Modify Network (the flow might change in some Android versions) and add proxy hostname as 192.168.56.1 and port 8080. Open the second emulator device, continue the same process but add the port as 8081.\nMake sure you install Burp CA on both the emulators.\nNow you have setup the pentest environment for the app. Open the app in both the emulators and you could see the traffic rushing through BurpSuite. The traffic includes HTTP calls to the target domain and sometimes its subdomains, a bunch of analytics services and the Android phone’s connectivity checks.\nFor an example, let us open https://m.facebook.com on default browser in both the emulators.\nLots of network calls in a few seconds.\nIt’s easy to get confused on which app on the emulators made which HTTP request. Even if you are not confused, don’t worry, you would get lost in an ocean of HTTP requests sometime after playing with the app.\nNotice one interesting column called Listener port which would be hiding at the end of all columns by default. It maps the network calls with the BurpSuite attached ports through which they were proxied. This helps us understand which app on which emulator devices made the request. Later in the pentest, we could sort the HTTP requests based on the Listener port and check if some interesting requests were made by one app but not the other. We have setup the pentest environment, but its not efficient unless you tweak some BurpSuite settings.\nBurpSuite tweaks Android apps are always built with analytics enabled. Even if analytics is not enabled by the app, there’s always connectivity checks by Android itself. If you intercept all traffic, you would end up intercepting the uninteresting analytics and connectivity check HTTP calls. Coming back to our example, let’s say we want to target requests made to any subdomain of facebook.com, i.e *.facebook.com.\nAdding the target to scope Goto Target → Scope → Target Scope. Enable advanced scope control, click on Add button, enter .*\\.facebook\\.com$ in the Host or IP range and click OK. (If your target domain is targetdomain.com, then the hosts regex would be .*\\.targetdomain\\.com$ ). Now you can set the BurpSuite HTTP history tab to only show traffic for the target domain.\nIntercepting HTTP requests only to the target requests: Even after you add the target domains to scope, network calls to other non-target domains will be intercepted by default if the \u0026ldquo;Intercept\u0026rdquo; is on. To enable interception only on the target domains, goto Proxy → Options → Intercept Client Requests, click add, enter the same regex we had used earlier and click OK. In this example, the regex would be .*\\.facebook\\.com$ .\nDownload links: Genymotion : https://www.genymotion.com/fun-zone/ * requires registration BurpSuite Community Edition : https://portswigger.net/burp/communitydownload This article was originally published at AndroidTamer Medium publication. AndroidTamer is an open source virtual platform for Android Security Professionals.\n","tags":["Android","BurpSuite"],"title":"Efficient way to pentest Android Chat Applications"},{"categories":null,"date":"October 23, 2018","permalink":"https://badshah.io/blog/managing-linux-users-ssh-keys-using-ansible/","section":"blog","summary":"Today I was assigned a task to create user accounts on an EC2 instance (Ubuntu) and also add SSH public keys to the respective user account’s authorized key list. The EC2 instance would act as a gateway to access the internal network. (This is a basic setup in which the user creates an SSH tunnel to access resources on the internal network. It’s not a foolproof security solution but controls external access to some extent)\nAfter getting the gist of the problem statement one could go right away, get the SSH public keys of users and create new user accounts on a new/existing instance. This solution seems working (yes, it does work) but in the long run, managing multiple user accounts and their SSH keys would be complicated and error prone.\nLet’s deeply analyze the problem statement:\nWhenever a new user needs access to the internal network, a Linux user account must be created and his/her SSH public key should be added to the authorized keys list If the user doesn’t require access to the internal network anymore, the user account should be revoked immediately No data is stored on the EC2 instance as it is only used for SSH tunneling. The problem statement involves less manual effort (limited to getting the usernames \u0026amp; SSH keys and maintaining the list of users who are allowed to access the resources). Solution to this problem statement could be automated.\nI felt that instead of deleting specific user accounts after explicit revocation (which involves maintaining a list of revoked usernames), it’s a good idea to delete all users and re-add only the required users. This would avoid the blunder of not revoking the access of an old user who is now unauthorized to access the instance.\nAnsible script to solve the above problem would look like the following:\n--- - hosts: production gather_facts: no # This is helpful if a new EC2 instance is to be provisioned become: yes vars: - default_users: [\u0026#39;nobody\u0026#39;] - required_users: [\u0026#39;badshah\u0026#39;, \u0026#39;bob\u0026#39;, \u0026#39;alice\u0026#39;] tasks: - name: Check python raw: test -e /usr/bin/python || (apt -y update \u0026amp;\u0026amp; apt install -y python-minimal) changed_when: false - name: Get list of all users shell: \u0026#34;getent passwd | awk -F: \u0026#39;$3 \u0026gt; 1000 {print $1}\u0026#39;\u0026#34; changed_when: false register: users - name: Remove all users user: name: \u0026#34;{{ item }}\u0026#34; state: absent remove: yes with_items: \u0026#34;{{ users.stdout_lines }}\u0026#34; when: item not in default_users - name: Add required users user: name: \u0026#34;{{ item }}\u0026#34; state: present with_items: \u0026#34;{{ required_users }}\u0026#34; - name: Add SSH public keys authorized_key: user: \u0026#34;{{ item }}\u0026#34; state: present key: \u0026#34;{{ lookup(\u0026#39;file\u0026#39;, \u0026#39;keys/{{ item }}\u0026#39;) }}\u0026#34; with_items: \u0026#34;{{ required_users }}\u0026#34; Explanation If one is provisioning a new EC2 Ubuntu 18.04 Linux instance, python is not installed by default. That’s why the first task checks for python and installs a minimal version of python if it isn’t installed (test -e /usr/bin/python || (apt -y update \u0026amp;\u0026amp; apt install -y python-minimal)). Ansible gathers facts about the remote system by default. If python isn’t installed, it runs into error even before starting the first task. So we disable it using gather_facts: no.\nNext step is to remove all the Linux users. But this is not a straightforward step as Linux machines have multiple users by default to handle multiple background tasks. One can use the command getent passwd | awk -F: '$3 \u0026gt; 1000 {print $1}', to get all the Linux users with UID greater than 1000 i.e. all the local and remote users of the machine. (The user nobody is a pseudo user and has least permissions on the system. Lets add it to a whitelist called default_users and lets not touch it). Then delete all the Linux user accounts returned from the above command only if it isn’t explicitly mentioned in the default_users array.\nNow we have deleted all the user accounts. The remaining task is to create the required users and add their SSH public keys to authorized list. Adding new users and gathering their SSH public keys is the only manual step. Ansible has modules like user and authorized_key which allows managing user accounts and authorized SSH keys respectively. Let’s create a list called required_users which would contain the names of the user accounts to be present.\nAfter a user account is created using the user module, SSH public key could be added from a file. In our case, as there are multiple user accounts, its better to maintain each SSH key in a separate file. {{ lookup(‘file’, ‘keys/{{ item }}’) }} tells Ansible to get the SSH key from a file saved with the same name as the user, in the “keys” directory.\nIf we need to add a new user, we just have to append the name in required_users and add the SSH key under the keys directory with the same name. If you want to remove the user, just delete the name from the required_users list and the Ansible script will remove the user for you.\nAlso thanks to Stack Overflow : https://stackoverflow.com/questions/37441796/ansible-for-user-management-removing-dead-accounts\n","tags":["Ansible","AWS","AWS EC2"],"title":"Managing Linux Users \u0026 SSH keys using Ansible"},{"categories":null,"date":"August 3, 2018","permalink":"https://badshah.io/blog/bucket-policy-for-your-public-s3-bucket/","section":"blog","summary":"Recently I came across multiple AWS S3 buckets with directory listing enabled. The content in the buckets ranged from simple images \u0026amp; js files to images of aadhaar ID, PAN cards, etc.\nWhats the reason ? Security is a non-functional requirement of business. What I have seen so far is that if a developer gets an idea, he/she will work to implement the idea without thinking much about the security of the product. But in the long term when the product matures, even small misconfigurations could lead to huge security vulnerabilities.\nWhom to blame if the S3 bucket is public ? Amazon does a very good job in showing if a bucket is public or not. When a bucket is made public, you can see a tag under “Permissions” which says that the bucket is public.\nIf you ever come across a developer / maintainer who says that he / she didn’t know that the bucket was public, just DONT TRUST because Amazon tells them its public everytime they see the Management Console.\nBut its not just the fault of user. If an inexperienced person wants to create S3 bucket with content that should be readable by all users, he/she would goto “Permissions”, check for something similar to Read Access (Public access with List Objects) and will enable it.\nWhat happens when the user performs this action is that the files are readable by everyone, at the same time files in every directory is also publicly listed when you visit http://bucketname.s3.amazonaws.com.\nSolution One can use S3 bucket policy to enable only the required actions (like GetObjects, PutObjects, etc). I am giving out the S3 bucket policy which I personally use. What this policy does is, it allows unauthenticated public access to the objects in the bucket but disables directory listing. Putting in simple terms, a user could access a file in S3 if he/she knows the exact location of the file.\n{ \u0026#34;Version\u0026#34;: \u0026#34;2012-10-17\u0026#34;, \u0026#34;Statement\u0026#34;: [ { \u0026#34;Sid\u0026#34;: \u0026#34;Stmt1405592139000\u0026#34;, \u0026#34;Effect\u0026#34;: \u0026#34;Allow\u0026#34;, \u0026#34;Principal\u0026#34;: \u0026#34;*\u0026#34;, \u0026#34;Action\u0026#34;: \u0026#34;s3:GetObject\u0026#34;, \u0026#34;Resource\u0026#34;: [ \u0026#34;arn:aws:s3:::bucketname/*\u0026#34;, \u0026#34;arn:aws:s3:::bucketname\u0026#34; ] } ] } Note: Replace bucketname with the name of your S3 Bucket.\nIf you have a better solution than this, please let me know in the comments.\n","tags":["AWS","AWS S3","Misconfigurations"],"title":"Bucket Policy for your Public S3 Bucket"},{"categories":null,"date":"January 1, 0001","permalink":"https://badshah.io/portfolio/explore/","section":"portfolio","summary":"","tags":null,"title":"Explore"},{"categories":null,"date":"January 1, 0001","permalink":"https://badshah.io/portfolio/gooir/","section":"portfolio","summary":"","tags":null,"title":"Gooir"},{"categories":null,"date":"January 1, 0001","permalink":"https://badshah.io/portfolio/kana/","section":"portfolio","summary":"","tags":null,"title":"Kana"},{"categories":null,"date":"January 1, 0001","permalink":"https://badshah.io/portfolio/mozar/","section":"portfolio","summary":"","tags":null,"title":"Mozar"},{"categories":null,"date":"January 1, 0001","permalink":"https://badshah.io/portfolio/stay-fit/","section":"portfolio","summary":"","tags":null,"title":"Stay Fit"},{"categories":null,"date":"January 1, 0001","permalink":"https://badshah.io/portfolio/zorro/","section":"portfolio","summary":"","tags":null,"title":"Zorro"}]