[{"data":1,"prerenderedAt":802},["ShallowReactive",2],{"writing-\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat":3},{"id":4,"title":5,"body":6,"date":791,"description":792,"excerpt":793,"extension":794,"image":36,"meta":795,"navigation":796,"path":797,"readTime":798,"seo":799,"stem":800,"__hash__":801},"writing\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat.md","Google CTF Competition 2018: Cat Chat",{"type":7,"value":8,"toc":775},"minimark",[9,27,30,37,40,45,58,70,79,88,94,97,114,123,131,139,143,154,160,177,185,192,199,205,208,211,217,224,268,277,281,284,288,294,297,301,304,310,313,349,353,366,370,380,387,397,403,407,410,413,427,451,483,489,517,521,528,534,540,543,600,604,624,630,633,639,642,646,649,664,667,671,678,684,687,696,700,709,768,771],[10,11,12,13,20,21,26],"p",{},"This past weekend Google held the qualification round for its third annual ",[14,15,19],"a",{"href":16,"rel":17},"https:\u002F\u002Fsecurity.googleblog.com\u002F2018\u002F05\u002Fgoogle-ctf-2018-is-here.html",[18],"nofollow","Capture the Flag competition",", with 25 demanding challenges in categories ranging from cryptography to web to binary exploits. While my skillset goes nowhere near close to that of the ",[14,22,25],{"href":23,"rel":24},"https:\u002F\u002Fctftime.org\u002Fevent\u002F623",[18],"talented teams"," participating from all over the world, I had some time to try my hand at one of the web challenges, Cat Chat, and wanted to document my approach. In my opinion, CTFs can be a great learning experience and taking a stab at some challenges as well as reading people's write-ups after can be great for becoming a better engineer.",[10,28,29],{},"This challenge wasn't trivial, but in the end I was able to get the flag:",[10,31,32],{},[33,34],"img",{"alt":35,"src":36},"Google CTF flag display","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fflag-unlocked.webp",[10,38,39],{},"Read on to find out how!",[41,42,44],"h2",{"id":43},"starting-out","Starting Out",[10,46,47,51,52,57],{},[48,49,50],"em",{},"Cat Chat"," reminded me of IRC hacking challenges popular a decade ago, like the ones on ",[14,53,56],{"href":54,"rel":55},"https:\u002F\u002Fwww.hackthissite.org\u002F",[18],"HackThisSite",". I'll let the screenshot speak for itself, but the premise is that there's a chat app run by someone bent against canines, and the goal is to (surprise!) steal the admin's credentials to get the flag.",[59,60,61,62,61,66],"figure",{},"\n  ",[33,63],{"src":64,"alt":65},"\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fcat-chat-ui.webp","Cat Chat UI",[67,68,69],"figcaption",{},"Sprinkle on a sidebar and some emojis, and you'll have Slack. :)",[10,71,72,73,78],{},"Reading over the preface, it looks like we get access to the ",[14,74,77],{"href":75,"rel":76},"https:\u002F\u002Fexpressjs.com\u002F",[18],"Express"," server's source code! And naturally we have access to the client's source as well. In general with these types of challenges, an effective approach is to first explore the app itself, and then do a code review and spot any weaknesses that may have been (usually intentionally) introduced. [I'll denote those with a 🚩.]",[59,80,61,81,61,85],{},[33,82],{"src":83,"alt":84},"\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fchange-name-prompt.webp","Change name prompt",[67,86,87],{},"The script kiddy's favorite name, age, and location.",[10,89,90],{},[33,91],{"alt":92,"src":93},"Script injection attempt","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fscript-injection-failed.webp",[10,95,96],{},"To start things off, we can change our name 🚩 to anything, so why not do the quick-n-dirty inline script test? Unfortunately, no dice — the input is escaped.",[10,98,99,100,103,104,109,110,113],{},"Moving on, the admin ",[48,101,102],{},"really",", ",[48,105,106],{},[107,108,102],"strong",{}," hates dogs. As a test, let's see what happens when we join the same room as another user (i.e., in an incognito window) and start talking about dogs. The admin comes in, bans anyone who utters the word (ahem, ",[107,111,112],{},"red_bombay","), and promptly disconnects. That's interesting — we can summon the admin to our chatroom at any time. Once we figure out what exactly makes the admin an admin, maybe we can somehow compromise them?",[10,115,116,117,122],{},"Before we go on, let's also look at one more thing: whether the client sets a ",[14,118,121],{"href":119,"rel":120},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FHTTP\u002FCSP",[18],"Content Security Policy",". If it does (as any modern web app should), that will significantly decrease our client-side attack vectors.",[59,124,61,125,61,129],{},[33,126],{"src":127,"alt":128},"\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fcsp-headers-network.webp","Network tab in Chrome's Developer Tools",[67,130,128],{},[10,132,133,134,138],{},"Sure enough, the CSP here is pretty strict. Even if we find an opportunity to inject some JavaScript into the page, the CSP will prevent the browser from executing it. The only thing that's of interest is the ",[135,136,137],"code",{},"'unsafe-inline'"," CSS policy 🚩, but how often can a stylesheet be used in an attack? 🤨",[41,140,142],{"id":141},"inspecting-the-client","Inspecting the client",[10,144,145,146,149,150,153],{},"Looking at the main page's source code, we get a huge hint about two secret commands 🚩, ",[135,147,148],{},"\u002Fsecret"," and ",[135,151,152],{},"\u002Fban",":",[10,155,156],{},[33,157],{"alt":158,"src":159},"Client secret commands","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fclient-secret-commands.webp",[10,161,162,163,166,167,169,170,172,173,176],{},"We now know ",[48,164,165],{},"how"," the admin actually bans unwelcome 🐕 supporters, and more importantly we know ",[48,168,165],{}," to become the admin. Running the ",[135,171,148],{}," command authenticates the user; we'll later discover that using it sets a cookie named ",[135,174,175],{},"flag"," in the browser 🚩:",[59,178,61,179,61,183],{},[33,180],{"src":181,"alt":182},"\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fchrome-application-cookies.webp","Chrome's Application tab in Developer Tools",[67,184,182],{},[10,186,187,188,191],{},"So, to get the flag ",[107,189,190],{},"we have to steal the admin's secret cookie",". Easier said than done!",[10,193,194,195,198],{},"Let's proceed further and look at the client, ",[135,196,197],{},"catchat.js",". First thing that we notice is the admin is quite lazy, running a function to look for the word \"dog\" and ban anyone who said it.",[10,200,201],{},[33,202],{"alt":203,"src":204},"Client admin ban logic","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fclient-admin-ban-logic.webp",[10,206,207],{},"We know that we can change our name to anything, so let's keep that in mind — the admin will repeat any name that we give it 🚩.",[10,209,210],{},"Proceeding further, we can see how all the different types of messages are handled:",[10,212,213],{},[33,214],{"alt":215,"src":216},"Client message handlers","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fclient-message-handlers.webp",[10,218,219,220,223],{},"Rats — the code escapes all untrusted user input with an ",[135,221,222],{},"esc()"," function, so we can't just start writing our own HTML. But there are still a few things of interest here:",[225,226,227,242,249],"ol",{},[228,229,230,231,233,234,237,238,241],"li",{},"Whenever someone runs the ",[135,232,148],{}," command, the password gets stashed under a ",[135,235,236],{},"\u003Cspan>"," into its ",[135,239,240],{},"data-secret"," attribute. So in addition to living in the cookie, the secret also gets written into the page. 🚩",[228,243,244,245,248],{},"Banning is strictly a client-side thing — the server just sets a ",[135,246,247],{},"banned"," cookie that we can easily clear out. 🚩",[228,250,251,252,255,256,259,260,263,264,267],{},"Remember how a banned user becomes red in the chat room? Apparently that's done using some CSS that matches all divs whose ",[135,253,254],{},"data-name"," attribute ",[48,257,258],{},"starts with"," the banned user's name 🚩. Looks like the developer forgot to quote the name though, and there's no good reason to use ",[135,261,262],{},"^="," instead of ",[135,265,266],{},"="," for the predicate. ¯\\_(ツ)_\u002F¯",[10,269,270,271,276],{},"Other than that, the code looks reasonably robust. The rest of it deals with handling reporting user a ",[14,272,275],{"href":273,"rel":274},"https:\u002F\u002Fwww.google.com\u002Frecaptcha\u002Fintro\u002Fv3beta.html",[18],"reCAPTCHA"," to prevent spamming the admin and generating a new name upon connection. We've gleaned enough from here, so let's hop over and look at the server's source code.",[41,278,280],{"id":279},"inspecting-the-server","Inspecting the server",[10,282,283],{},"Looking through the server's source resulted in a number of interesting observations.",[285,286,121],"h3",{"id":287},"content-security-policy",[10,289,290],{},[33,291],{"alt":292,"src":293},"Server CSP definition","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fserver-csp-definition.webp",[10,295,296],{},"As we saw earlier, the server sets a CSP to help prevent cross-site scripting and the like.",[285,298,300],{"id":299},"incoming-messages-handler","Incoming Messages Handler",[10,302,303],{},"This handler is the meat of the chat server; it handles all incoming messages:",[10,305,306],{},[33,307],{"alt":308,"src":309},"Server message router","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fserver-message-router.webp",[10,311,312],{},"Already, a number of interesting things here:",[225,314,315,318,321,346],{},[228,316,317],{},"In general it's best practice to avoid using GETs for operations that result in change of state. But bizarrely enough, the application accepts messages for all HTTP verbs, including GET. That means that if we have someone simply visit a URL (or perhaps embed an image with that URL as the source), we can potentially get them to send a message. 🚩",[228,319,320],{},"The developer was at least somewhat prudent in implementing a basic CSRF protection mechanism. Without it, we could potentially stage a spear-phishing attack by sending the admin an interesting 🐈 article with a hidden surprise, but looks like that won't work because the referrer will be different than the host. 🤔",[228,322,323,324,327,328,331,332,335,336,339,340,342,343,345],{},"Ah, looks like the dev fell into the common mistake of forgetting a ",[135,325,326],{},"break"," in a ",[135,329,330],{},"switch-case"," statement! 🚩 The starting command is extracted correctly, but each command thereafter essentially only tests for the presence of the command word. Perhaps this was meant to be intentional, but that means typing something like ",[135,333,334],{},"\u002Fname \u002Fban red_bombay"," would change the name to ",[135,337,338],{},"\u002Fban red_bombay"," and then issue a ban command to ban ",[135,341,112],{},". Since we control the text after ",[135,344,152],{}," by changing our name to whatever, we can make the admin change their secret (locking them out of banning) and then have them report the room again.",[228,347,348],{},"Setting the secret apparently just writes that password into the header directly, without URI-encoding it. 🚩 We probably can't use it to set another header, but this might still come in useful...",[41,350,352],{"id":351},"orchestrating-the-attack","Orchestrating the attack",[10,354,355,356,359,360,362,363,365],{},"Our goal is to get the flag from the admin's cookie, and since the CSP prevents any reasonable attempt at writing JavaScript to steal ",[135,357,358],{},"document.cookie",", the only way we can steal it is via that ",[135,361,240],{}," attribute set when running the ",[135,364,148],{}," command. This password is only set once a year, so most likely that element doesn't even exist... but maybe we can trick the admin into running the command?",[285,367,369],{"id":368},"populating-the-secret","Populating the secret",[10,371,372,373,376,377,379],{},"Presumably if we change the name of our dog-loving user to something like ",[135,374,375],{},"\u002Fsecret \u003Cnew secret>",", we can get the admin to execute that command and fill in the ",[135,378,240],{}," attribute with that new secret. But wait! Doing so would erase the flag, right?",[10,381,382,383,386],{},"As it turns out, the cookie header's value is unescaped, meaning we can actually make the header invalid and thus prevent the browser from applying it. There are a number of ways of doing this, but perhaps the most straightforward is to simply set the domain value to something other than ",[135,384,385],{},"cat-chat.web.ctfcompetition.com",". For instance, writing:",[388,389,394],"pre",{"className":390,"code":392,"language":393},[391],"language-text","\u002Fname \u002Fsecret foo; domain=xyz.com\n","text",[135,395,392],{"__ignoreMap":396},"",[10,398,399,400,402],{},"would end up triggering the logic that populates ",[135,401,240],{}," with the cookie without actually overriding it. We're getting close!",[285,404,406],{"id":405},"extracting-the-secret","Extracting the secret",[10,408,409],{},"We now have a way of tricking the admin into filling the local DOM with the cookie we want to steal, but now we actually have to, well, steal it. We can't inject any scripts, but let's go back and see if there are any useful 🚩.",[10,411,412],{},"That 'inline-style' CSP policy looked suspicious, as did this piece of code that makes the banned user's name red:",[388,414,418],{"className":415,"code":416,"language":417,"meta":396,"style":396},"language-javascript shiki shiki-themes github-light github-dark","display(`${esc(data.name)} was banned.\u003Cstyle>span[data-name^=${esc(data.name)}] { color: red; }\u003C\u002Fstyle>`);\n","javascript",[135,419,420],{"__ignoreMap":396},[421,422,425],"span",{"class":423,"line":424},"line",1,[421,426,416],{},[10,428,429,430,103,433,103,436,439,440,443,444,447,448,450],{},"The name is escaped, meaning we can't use ",[135,431,432],{},"\u003C",[135,434,435],{},">",[135,437,438],{},"'",", or ",[135,441,442],{},"\""," (the dev forgot ",[135,445,446],{},"&","). But we can still close that rule and write any CSS that we want on the page. This may seem far-fetched, but would we be able to steal the contents of ",[135,449,240],{}," with a stylesheet? Putting 2 and 2 together, the idea is to trick the admin like so:",[452,453,454,464,472,480],"ul",{},[228,455,456,457,459,460,463],{},"If the ",[135,458,240],{}," content starts with ",[135,461,462],{},"A",", set the background-image to a logged URL containing \"A\"",[228,465,456,466,459,468,471],{},[135,467,240],{},[135,469,470],{},"B",", set the background-image to a logged URL containing \"B\"",[228,473,456,474,459,476,479],{},[135,475,240],{},[135,477,478],{},"C",", set the background-image to a logged URL containing \"C\"",[228,481,482],{},"... and so on",[10,484,485,486,488],{},"Then, once we get the first letter (let's say it's ",[135,487,478],{},"):",[452,490,491,499,507,515],{},[228,492,456,493,459,495,498],{},[135,494,240],{},[135,496,497],{},"CA",", set the background-image to a logged URL containing \"CA\"",[228,500,456,501,459,503,506],{},[135,502,240],{},[135,504,505],{},"CB",", set the background-image to a logged URL containing \"CB\"",[228,508,456,509,459,511,514],{},[135,510,240],{},[135,512,513],{},"CC",", set the background-image to a logged URL containing \"CC\"",[228,516,482],{},[285,518,520],{"id":519},"bypassing-the-csp","Bypassing the CSP",[10,522,523,524,527],{},"The next step is to actually log these requests, so that we know when we've guessed a letter correctly. Let's just spin up a quick Droplet and run ",[135,525,526],{},"python -m SimpleHTTPServer 80",", right?",[10,529,530,533],{},[48,531,532],{},"Not so fast!"," The server's CSP actually prevents embedding any external images. But, recall that we can actually send messages to any chatroom via a simple URL. So we could instead use an image source like:",[388,535,538],{"className":536,"code":537,"language":393},[391],"https:\u002F\u002Fcat-chat.web.ctfcompetition.com\u002Froom\u002F\u003Croom-id>\u002Fsend?name=name&msg=message\n",[135,539,537],{"__ignoreMap":396},[10,541,542],{},"To put this into context, the CSS rule would look like this:",[388,544,548],{"className":545,"code":546,"language":547,"meta":396,"style":396},"language-css shiki shiki-themes github-light github-dark","span[data-secret^=A] {\n  background: url(https:\u002F\u002Fcat-chat.web.ctfcompetition.com\u002Froom\u002F\u003Croom-id>\u002Fsend?name=the%20password%20is&msg=A)\n}\n","css",[135,549,550,571,594],{"__ignoreMap":396},[421,551,552,555,559,562,565,568],{"class":423,"line":424},[421,553,421],{"class":554},"s9eBZ",[421,556,558],{"class":557},"sVt8B","[",[421,560,240],{"class":561},"sScJk",[421,563,262],{"class":564},"szBVR",[421,566,462],{"class":567},"sZZnC",[421,569,570],{"class":557},"] {\n",[421,572,574,578,581,584,587,591],{"class":423,"line":573},2,[421,575,577],{"class":576},"sj4cs","  background",[421,579,580],{"class":557},": ",[421,582,583],{"class":576},"url",[421,585,586],{"class":557},"(",[421,588,590],{"class":589},"s4XuR","https:\u002F\u002Fcat-chat.web.ctfcompetition.com\u002Froom\u002F\u003Croom-id>\u002Fsend?name=the%20password%20is&msg=A",[421,592,593],{"class":557},")\n",[421,595,597],{"class":423,"line":596},3,[421,598,599],{"class":557},"}\n",[285,601,603],{"id":602},"proof-of-concept","Proof-of-concept",[10,605,606,607,149,610,613,614,619,620,623],{},"Provided that the character set of the flag is restricted (we'll assume uppercase letters, numbers, and ",[135,608,609],{},"{",[135,611,612],{},"}"," for now), let's try this out on ourselves first. We can't try too many characters at once ",[14,615,618],{"href":616,"rel":617},"https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc7230#section-3.1.1",[18],"because URLs can only be so big",". Note that to make the rest of the CSS valid we can simply end with a ",[135,621,622],{},"\u002F*",".",[10,625,626],{},[33,627],{"alt":628,"src":629},"CSS injection exploit","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fcss-injection-exploit.webp",[10,631,632],{},"That worked! Here's what happened:",[10,634,635],{},[33,636],{"alt":637,"src":638},"Secret exfiltration","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fsecret-exfiltration.webp",[10,640,641],{},"We successfully injected some CSS to trick the browser into issuing a request to post a message to the chat.",[285,643,645],{"id":644},"putting-it-all-together","Putting it all together",[10,647,648],{},"We've now identified a number of useful deficiencies in the code to put together a successful cookie-stealing attack. To make it all work, we'll want to trick the admin into running a command that will:",[225,650,651,661],{},[228,652,653,654,657,658,660],{},"Set their ",[107,655,656],{},"secret"," to something that won't actually override it but still set the ",[135,659,240],{}," attribute in the page. ✅",[228,662,663],{},"Inject some stylesheet rules that will log a request to a chat room based on the first (or nth) letter of the secret. ✅",[10,665,666],{},"Now, writing this payload by hand is laborious, so let's write a snippet to do it for us:",[668,669],"gist-embed",{"gist-id":670},"8547d242520567738e5ef8a0b9f9bb26",[10,672,673,674,677],{},"Let's start out by opening two chat sessions in the same room, changing the name of one of the users, issuing a ",[135,675,676],{},"\u002Freport",", and saying some poochy profanity. Success!",[10,679,680],{},[33,681],{"alt":682,"src":683},"CSRF exploit delivery","\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fcsrf-exploit-delivery.png",[10,685,686],{},"Rinse and repeat (clearing the banned cookie and refreshing each time in the banned user's session), and we get our flag:",[59,688,61,689,61,693],{},[33,690],{"src":691,"alt":692},"\u002Fimages\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat\u002Fpayload-generator-draft.png","Payload generator draft",[67,694,695],{},"Before I tidied up the payload generator. :)",[41,697,699],{"id":698},"learnings","Learnings",[10,701,702,703,708],{},"While clearly staged, this was a fun challenge and I enjoyed putting together the fabled character-by-character CSS attack (you may have seen ",[14,704,707],{"href":705,"rel":706},"https:\u002F\u002Fnews.ycombinator.com\u002Fitem?id=16422696",[18],"this CSS keylogger Hacker News post"," a few months ago). But there are some lessons to take away here:",[452,710,711,722,730,739,759,762,765],{},[228,712,713,714,717,718,721],{},"Set a ",[14,715,121],{"href":119,"rel":716},[18]," on your site to offer a last-resort protection against malicious user-injected content, and be extra careful about allowing access to sources you don't control, especially ",[135,719,720],{},"unsafe-inline",". For inline scripts and styles, use a nonce. (This mechanism will become more secure as additional browsers support CSP v3.)",[228,723,724,725,149,727,729],{},"Don't leak the mechanisms of your admin functionality. While relying on security through obscurity is never good, this attack would have been significantly harder to execute without knowing the ",[135,726,148],{},[135,728,152],{}," commands; a separate admin client would have been a good deterrent.",[228,731,732,733,738],{},"Follow the ",[14,734,737],{"href":735,"rel":736},"https:\u002F\u002Fwww.w3.org\u002F2001\u002Ftag\u002Fdoc\u002FwhenToUseGet.html#checklist",[18],"REST spec for defining endpoints"," — GET requests should be idempotent and never mutate any resources.",[228,740,741,742,750,751,753,754,623],{},"Similar to ",[14,743,746,747],{"href":744,"rel":745},"https:\u002F\u002Fxkcd.com\u002F292\u002F",[18],"avoiding ",[135,748,749],{},"goto",", be extra careful about complex ",[135,752,330],{}," statements, particularly jumping across cases (and if you have to, leave clear comments). Doing otherwise can ",[14,755,758],{"href":756,"rel":757},"https:\u002F\u002Fwww.synopsys.com\u002Fblogs\u002Fsoftware-security\u002Fgimme-a-break\u002F",[18],"set you up for failure",[228,760,761],{},"If you have to deal with banning users, don't rely on cookies; there are plenty of better alternatives (reCAPTCHAs, user accounts, Cloudflare, etc.).",[228,763,764],{},"Escape user input, always use quotes around attributes, and URI-encode cookie header contents (or use a library).",[228,766,767],{},"Don't hate on dogs. :)",[10,769,770],{},"If you made it this far, I hope you enjoyed reading this and took something out of it! And if I missed something, please let me know. Happy hacking!",[772,773,774],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":396,"searchDepth":573,"depth":573,"links":776},[777,778,779,783,790],{"id":43,"depth":573,"text":44},{"id":141,"depth":573,"text":142},{"id":279,"depth":573,"text":280,"children":780},[781,782],{"id":287,"depth":596,"text":121},{"id":299,"depth":596,"text":300},{"id":351,"depth":573,"text":352,"children":784},[785,786,787,788,789],{"id":368,"depth":596,"text":369},{"id":405,"depth":596,"text":406},{"id":519,"depth":596,"text":520},{"id":602,"depth":596,"text":603},{"id":644,"depth":596,"text":645},{"id":698,"depth":573,"text":699},"2018-06-25","This past weekend Google held the qualification round for its third annual Capture the Flag competition, with 25 demanding challenges in categories ranging from cryptography to web to binary exploits. While my skillset goes nowhere near close to that of the talented teams participating from all over the world, I had some time to try my hand at one of the web challenges, Cat Chat, and wanted to document my approach. In my opinion, CTFs can be a great learning experience and taking a stab at some challenges as well as reading people's write-ups after can be great for becoming a better engineer.",null,"md",{},true,"\u002Fwriting\u002Fgoogle-ctf-competition-2018-cat-chat",10,{"title":5,"description":792},"writing\u002Fgoogle-ctf-competition-2018-cat-chat","Pz1_-l7_-jituwXC3F9rD_S34LuOFZ37nEyAW-5XRZM",1782034563928]