diff --git a/public/api/articles.json b/public/api/articles.json index 95ac975..09267d6 100644 --- a/public/api/articles.json +++ b/public/api/articles.json @@ -5,6 +5,18 @@ "title": "Printing in Python", "desc":"How to print to the terminal in python", "contents": "*Introduction* This is my tutorial on how to print in python3. The print function is an easy way to show your users important information, design menus, and more. The print command is simple in it's usage. First you want to open a python interpreter. Next type the following: `print('Hello World')` After that you Hello World printed to the screen. You can check out more articles by scrolling down to the list of articles on this page." + }, + { + "id": 1, + "title": "Hiding Data on Linux", + "desc": "Showing you how to hide files within a file in linux.", + "contents":"*Steganography*Steganography is the art or practice of concealing a message, image or file within another message, image or file. There are many techniques to perform steganography, one of which is to hide messages on physical paper in the physical world. Another technique involves hiding messages in digital images or digital data, also called digital messages. Images are the typical victim used to hide information. Social steganography is when messages are hidden in the title and context of a video or image. Names or words may be misspelled that are popular in a given time to suggest alternative meanings, or pictures can be traced by paint or drawing tools. When information is hidden in telecommunication networks, this is called network steganography. Today steganography is mostly done on computers with tools such as steghide, stegstudio and more. One simple way of doing so is on linux, by combining a zip file and an image in order to hide the in the back part of the image file.*Hiding Files Inside of a JPG File* You can first download an image that you'd like to use. Next, you'll create a file to hide. You can use anything, given that it can be hidden within a zip. I'm using a text file called steg.txt. Next, you'll want to compress the file. You can do this in the terminal by running the following. `zip zipfile.zip steg.txt` Now that you have a zip file you can now hide the file. We'll do this by using the cat command. This command will combine the data within two files, and allow us to create a new file with the data concatenated. Run the following command using image.jpg as our image file and zipfile as the zip we just created. If you have a file with a different file extension (I.E. .jpg, .png) change StegFile.jpg to reflect this change in addition to the original image file. `cat image.jpg zipfile.zip > StegFile.jpg` Now that everything is combined, you can attempt to view the StegFile.jpg image to see if you've correctly combined the two files. If you'd like to get the files out of the zip/steg file, you can then run the following on the image. `unzip StegFile.jpg` As you can see, it just unzipped the steg.txt from the image and placed it in the current directory. *Conclusion* Steganography is an interesting concept, especially revolving computer forensics and can be used to hide files and make interesting puzzles. *Sources* ~ https://www.merriam-webster.com/dictionary/steganography ~ ~ https://en.wikipedia.org/wiki/Steganography#Techniques ~ ~ http://steghide.sourceforge.net/ ~ ~ http://stegstudio.sourceforge.net/ ~" + }, + { + "id": 2, + "title": "(POC) Cracking Wifi using Phone Numbers", + "desc": "Showing you how to crack wifi passwords using phone numbers.", + "contents":"*Introduction*This article is a proof of concept and should show you why it's dangerous to use your phone number as your wifi password. Since the introduction of wifi, people have tried breaking into and exploiting it. WEP an old edition of wifi password security was a very weak method and could be easily broken just by sniffing wifi traffic coming to/from the AP. Since the introduction of WPA security, cracking has become more and more difficult. Although, if you can find the right password, one could potentially crack the login for wireless networks. This is done by sniffing for a specific traffic containing an EAPoL or Extensible Authentication Protocol over Lan. This essentially contains an hashed version of the wifi password. Using this we can then bruteforce the password and crack the password. As found in another article (see references) many people use their phone numbers as their wifi passwords. This can make it specifically easy to crack since your phone number is often related to your location through your area code. For example, if I was living in New York City, my area code would be either 212 or 718. If the wifi's password hash was sniffed somewhere in NYC, every phone number could be stored in a file around 200MB in size. With the technology of today, that password would be cracked in a matter of minutes if you used a phone number as your password. *Prerequisits*~Linux~ ~Wifi Card~ ~git~ ~python3~ *Creating a Phone List* In order to create the list of phone numbers you'll need to use a tool that I created. You can grab the files by cloning the repository `git clone https://github.com/RaspberryProgramming/phone-wordlist-generator` Enter the folder by running `cd phone-wordlist-generator` Using this you can generate every phone number in your area code and put it into a file. Replacing AREA with your area code run `python3 main.py --staticnum AREA` You will then find the phone numbers in a file called phones.list *Capturing Wifi Hashes* You might be wondering, how would I even get these password hashes? Theres a simple tool called aircrack-ng that gives you a suite of tools that you'll need. If you're running Ubuntu Linux you can run `sudo apt install aircrack-ng` To install all of the necessary tools. On Arch/Manjaro run `sudo pacman -Sy aircrack-ng-git` Now that you've got aircrack installed, you can now set your wifi card in monitor mode. Run `ifconfig` You'll get something similar to the following output `wlan0: flags=4163 mtu 1500\ninet 192.168.0.2 netmask 255.255.255.0 broadcast 192.168.0.255\nether db:3d:4d:b5:ff:12 txqueuelen 1000 (Ethernet)\nRX packets 130672 bytes 152955605 (152.9 MB)\nRX errors 0 dropped 0 overruns 0 frame 0\nTX packets 40063 bytes 8409394 (8.4 MB)\nTX errors 0 dropped 0 overruns 0 carrier 0 collisions 0`In my case, I'll be using the wlan0 for any sniffing. This may be unneccesary, but I'll be killing any conflicting processed by running. `sudo airmon-ng check kill` Run the following to put your wifi card in monitor mode `sudo airmon-ng start wlan0` You should now have a wlan0mon which is the monitor interface for the wlan0 card. If you had something longer like wlx... it may be the same as the original name. Now that you created the monitor interface, you can now start the sniff traffic. You can do this by running airodump. Replace INTERFACE with your monitor interface. The -w argument specifies the filename we'll be storing the capture to. The --output-format argument specifies we want a pcap file. `sudo airodump-ng --output-format pcap -w capfile INTERFACE` After a while, you may start to capture handshakes. There are multiple ways to determine this but the way I'll go over is using aircrack-ng. You may have multiple capture files if you ran the command multiple times, so we'll run ls to find the file. `ls` Look for a file with the extension .pcap. Next, run aircrack-ng with FILENAME as the filename of your capture file `sudo aircrack-ng FILENAME` You should see a list of SSIDs similar to the following output `Reading packets, please wait...\nOpening capfile-02.cap\nRead 370 packets.\n\n # BSSID ESSID Encryption\n\n 1 00:5F:67:FB:48:FC Unknown\n 2 64:05:E4:6A:E1:2A CarPlay_e12a WPA (1 handshake)\n 3 B6:BC:1F:14:72:0B AndroidAP_6374 WPA (0 handshake)\n 4 BC:82:5D:57:FC:AC WiFi Hotspot 4877 Unknown\n 5 C6:D4:38:D1:4A:2A Unknown\n 6 F8:55:CD:67:54:E0 HotspotLftY Unknown\n 7 F8:55:CD:68:0A:1F Truck WiFi Unknown\n\nIndex number of target network ? 1` If you see, we have 1 handshake from CarPlay_e12a. We can then use the wordlist we made before to try to crack the password. Run aircrack with the wordlist argument `sudo aircrack-ng -w phones.list` You can then select the ssid or wifi name with the handshake to start cracking. If you have the password you'll see the following message `KEY FOUND: 1839231234`" } ] } \ No newline at end of file diff --git a/public/index.html b/public/index.html index d5c0374..b5ab2d4 100644 --- a/public/index.html +++ b/public/index.html @@ -41,4 +41,16 @@ To create a production bundle, use `npm run build` or `yarn build`. --> + + + diff --git a/src/components/App.js b/src/components/App.js index 28612c9..7064361 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -4,6 +4,7 @@ import "./css/App.css"; import Navigation from './Navigation'; import Github from './Github'; import Articles from './Articles'; +import ArticleEditor from './ArticleEditor'; import About from './About'; import Intro from './Intro'; import Bai from './Bai'; @@ -20,6 +21,7 @@ const App = (props) => { + diff --git a/src/components/Article.js b/src/components/Article.js index 4fa9c40..e28f8ff 100644 --- a/src/components/Article.js +++ b/src/components/Article.js @@ -1,36 +1,147 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import Theater from './subcomponents/Theater'; import './css/Articles.css'; const Article = ({article}) => { + const [show, setShow] = useState(""); + const [currArticle, setCurrArticle] = useState(""); + + useEffect(()=>{ + if (currArticle === ""){ // If no articles have been opened + if (show === "") { + // Set the current article and initiate the open animation + setCurrArticle(article); + setShow("open"); + + // Once the animation runs, open the entire article + setTimeout(() =>{ + setShow("show"); + }, 1024) + } + + } else if (currArticle !== article) { // If we've changed articles + // Change currArticle to the actual current article and initiate close animation + setCurrArticle(article); + setShow("close"); + + // Step through the close animation, open animation, and fully opening the article + setTimeout(()=>{ + setShow(""); + + setTimeout(()=>{ + + setShow("open"); + + setTimeout(() =>{ + setShow("show"); + }, 1024); + }, 1024); + }, 24); + } + }, [currArticle, article, show]); + + let linkProcessor = (text) => { + /** + * Given some text, processes and returns jsx with any link represented as an anchor + */ + let output = [""]; // Stores all text in a list + let loc = 0; // Stores the current location in output that we're working with + let tmp; + let i = 0; + console.log(text); + while (i < text.length) { // Iterate through the entire text string + if (text.slice(i, i+4) === "http"){ // slice from i to 4 chars plus and check for http + let x = i; // store i in x so the location is not modified + + for (let y = i; ![" ", "\n"].includes(text[y]) && y < text.length; y++){ + i=y; + } // iterate until we find the end of the link denoted by a space + + if (output[loc] !== "") { // if the current output location isn't empty, increment loc + loc++; + } + tmp = text.slice(x, i+1); + // Put anchor for link into output list + output[loc] = {tmp}; + output[++loc] = ""; // Create new location in output with empty string + } else { + // Append current char to output + output[loc] += text[i]; + + } + i++; + + } + + // Return the output + return output; + }; + + let newLineProcessor = (text) => { + /** + * Given some text, processes and returns jsx with line breaks + */ + let tmp; + let output = [""]; // Stores all text in a list + let loc = 0; // Stores the current location in output that we're working with + for (let i = 0; i < text.length; i++) { // Iterate through the entire text string + if (!React.isValidElement(text[i])) { + tmp = [""]; + loc = 0; + for (let j = 0; j < text[i].length; j++){ + + if (text[i].slice(j, j+1) === "\n"){ // slice from i to 4 chars plus and check for http + if (tmp[loc] !== "") { // if the current output location isn't empty, increment loc + loc++; + } + tmp[loc] =
; + tmp[++loc] = ""; + + } else { + // Append current char to output + tmp[loc] += text[i][j]; + + } + } + tmp[++loc] = ""; + } else { + tmp = text[i] + } + + output.push(tmp) + } + + // Return the output + return output; + }; + let articleFormatter = (text) => { - let output = []; - let type = []; - let ind = 0; - let tick=false; - let delimiters = ['', '`', '*']; + let output = [""]; // Used to store separate formatted text + let type = []; // Parallel to output list to signify format type + let ind = 0; // Denote index of output + let tick=false; // used to check if we're currently in formatted text. + let delimiters = ['', '`', '*', '~']; // Denotes characters used to format + let tmp; for (let i = 0; i < text.length; i++) { // Iterate through input - console.log(text[i]); - if (delimiters.indexOf(text[i]) !== -1) { // Detect Code Delimiter - + if (delimiters.indexOf(text[i]) >= 0) { // Detect Code Delimiter + console.log(text[i]) if (tick) { // Close the code section - console.log(1); output[++ind] = "" tick = false; + tmp = ""; - } else { // Start a new code section - console.log(2); + } else { // Start a new code section type.push(delimiters.indexOf(text[i])); - if (!output[ind]) { + if (output.length < ind) { output[ind] = ""; } else if (output.length < type.length) { output[++ind] = ""; } - + tmp = ""; tick = true; } @@ -40,36 +151,41 @@ const Article = ({article}) => { if (output.length > type.length) { // If this is the beggining of a default text type type.push(0); - output[ind] = "" + //output[ind] = text[i] + } else if (output.length < type.length) { + output[++ind] = "" } - console.log(3); - - - output[ind] += text[i] + tmp = text[i] } - } - console.log(output); - console.log(type); + output[ind] += tmp + } return [...output.keys()].map((i)=>{ // Format text and return as jsx + let text = linkProcessor(output[i]); // Process links + text = newLineProcessor(text); if (type[i] === 0){ // Return default text type - return
{output[i]}
; + return
{text}
; } else if(type[i] === 1) { // Return Code text type - return
{output[i]}
; + return
{text}
; } else if (type[i] === 2) { - return
{output[i]}
; + return
{text}
; - }else { + } else if (type[i] === 3) { + + return
  • {text}
  • ; + + + } else { return
    ; } }); @@ -77,7 +193,7 @@ const Article = ({article}) => { }; return ( -
    +
    { + const [content, setContent] = useState("Hello World"); + + let article = { + "id":"0", + "title": "Article Editor", + "desc":"This is a place to edit articles", + "contents": content + }; + + let copyToClipboard = () => { + navigator.clipboard.writeText(content).then(function() { + console.log('Async: Copying to clipboard was successful!'); + }, function(err) { + console.error('Async: Could not copy text: ', err); + }); + }; + + return ( +
    +
    +
    Copy to Clipboard
    +
    +
    + +
    + ); +}; + +export default ArticleEditor; \ No newline at end of file diff --git a/src/components/Github.js b/src/components/Github.js index 15abaaf..aa33e0d 100644 --- a/src/components/Github.js +++ b/src/components/Github.js @@ -51,6 +51,8 @@ class Github extends React.Component { if (this.props.repos.length > 0) { // Render each repo const render = this.props.repos.map((repo) =>{ + let updated = (new Date (repo.updated_at)).toLocaleString(); + let created = (new Date (repo.created_at)).toLocaleString(); return (
    @@ -74,6 +76,11 @@ class Github extends React.Component { this.renderLanguages(repo.name) // Render each language for the repo }
    +
    + Last Updated: {updated} +
    + Created: {created} +
    ); diff --git a/src/components/Intro.js b/src/components/Intro.js index e392a5a..a9d9698 100644 --- a/src/components/Intro.js +++ b/src/components/Intro.js @@ -1,7 +1,6 @@ import React from 'react'; import { connect } from 'react-redux'; import './css/Intro.css'; -import {hideNavigation, showNavigation} from '../actions'; import Topic from './subcomponents/Topic'; import { ChevronDoubleUp, ChevronDoubleDown } from 'react-bootstrap-icons'; @@ -108,8 +107,6 @@ class Intro extends React.Component { } else if (input === "down" && this.state.nextLoc >= this.topics.length) { // Set something to happen when you reach the end - this.props.showNavigation(); - localStorage.setItem('intro', true); } }; @@ -123,11 +120,6 @@ class Intro extends React.Component { document.title = "HomePage"; // Set document title - // Hide the navigation - if (!localStorage.getItem('intro')) { - this.props.hideNavigation(); - } - } // Topics to display in intro @@ -137,7 +129,7 @@ class Intro extends React.Component {
    This website helps you access the projects that I've worked on. You can navigate at the top to different locations in the site. Within you can find information about me, - my github repositories, and some youtube videos I've posted. This website is coded with + my github repositories, and articles I've posted. This website is coded with React/Redux and hosted over Vercel. You can email me at cam@camscode.com.
    , @@ -178,4 +170,4 @@ const mapStateToProps = (state) => { } -export default connect(mapStateToProps, {hideNavigation, showNavigation})(Intro); +export default connect(mapStateToProps, {})(Intro); diff --git a/src/components/Navigation.js b/src/components/Navigation.js index fd3f365..f5745aa 100644 --- a/src/components/Navigation.js +++ b/src/components/Navigation.js @@ -7,39 +7,36 @@ import { HouseDoor, FileEarmarkPerson, Github, Envelope, Book } from 'react-boot import ContactModal from './subcomponents/ContactModal'; const Navigation = (props) => { - if (props.enable){ - return ( -
    - - Home - - - - Github - - - - Articles - - - - About - - - - -
    - ); - } + return ( +
    + + Home + + + + Github + + + + Articles + + + + About + + + + +
    + ); - return
    ; } const mapStateToProps = (state) => { - return {modal: state.contactModal.contactModal, enable: state.navigation.enable}; + return {modal: state.contactModal.contactModal}; } -export default connect(mapStateToProps, {toggleContactModal})(Navigation); +export default connect(mapStateToProps, {toggleContactModal})(Navigation); \ No newline at end of file diff --git a/src/components/css/About.css b/src/components/css/About.css index 8914400..eec3836 100644 --- a/src/components/css/About.css +++ b/src/components/css/About.css @@ -31,7 +31,7 @@ display:flex; flex-direction:row; align-content:center; - justify-content:flex-start; + justify-content:space-evenly; align-items:center; flex-wrap: wrap; width:85vw; @@ -44,4 +44,10 @@ .About .links * { margin: 10px; +} + +@media only screen and (max-width: 600px) { + .About .links .link img { + width: 25vw; + } } \ No newline at end of file diff --git a/src/components/css/ArticleEditor.css b/src/components/css/ArticleEditor.css new file mode 100644 index 0000000..43dd00e --- /dev/null +++ b/src/components/css/ArticleEditor.css @@ -0,0 +1,42 @@ +.ArticleEditor textarea { + width:100%; + height: 35vh; + border-style: solid; + border-color: lightgray; + border-radius: 2px; + border-width: 2px; + padding: 0.4em 0.4em 0.4em 0; + text-align: left; +} + +.ArticleEditor .toolbar { + width: 100%; + height: 56px; + display: flex; +} + +.ArticleEditor .toolbar .btn { + color: white; + background-color: darkgreen; + text-align: center; + align-items:center; + justify-content: center; + display: flex; + padding-left: 3px; + padding-right: 3px; +} +.ArticleEditor .toolbar .btn:hover{ + background-color: #005400 +} + +.ArticleEditor .article .open { + max-height:100vh; +} + +.ArticleEditor .article .Close { + max-height:100vh; +} + +.ArticleEditor .article { + max-height:100vh; +} \ No newline at end of file diff --git a/src/components/css/Articles.css b/src/components/css/Articles.css index ca63e41..24d8038 100644 --- a/src/components/css/Articles.css +++ b/src/components/css/Articles.css @@ -3,24 +3,51 @@ align-items: center; display: flex; flex-direction: column; + max-height:0; + overflow: hidden; + transition: max-height 1s ease; + +} + +.article.open { + max-height:100vh; + transition: max-height 1s ease; +} + +.article.close { + max-height:100vh; +} + +.article.show { + max-height:none; + } .article .content { width: 80vw; text-align:left; + line-height: 1.5; } .article .code { padding: 10px; background-color: #3A3B3B; - color: gray; + color: lightgray; border-radius: 5px; - margin: 10px; + margin: 20px; } .article .h1 { font-size: 24px; font-weight:bolder; - margin-top: 10px; + margin-top: 20px; margin-bottom: 10px; +} + +.article .li { + margin-left: 10px; +} + +.article a { + color: #ACACFF; } \ No newline at end of file diff --git a/src/components/css/Github.css b/src/components/css/Github.css index c72eacb..8173495 100644 --- a/src/components/css/Github.css +++ b/src/components/css/Github.css @@ -61,7 +61,7 @@ .repo .description { flex-grow: 1; - padding:10px; + padding:5px; } .repo .languages { @@ -82,6 +82,14 @@ color: #DAD4DF; } +.repo .times { + padding: 5px; + margin: 2px; + margin-left: 5px; + width: 100%; + text-align: start; +} + .content { margin: 25px; text-align: center; @@ -95,4 +103,11 @@ border-radius: 10rem; height: 10rem; width: 10rem; +} + +@media only screen and (max-width: 600px) { + + .repo .content { + flex-direction: column; + } } \ No newline at end of file diff --git a/src/components/css/Theater.css b/src/components/css/Theater.css index 0940f71..f912564 100644 --- a/src/components/css/Theater.css +++ b/src/components/css/Theater.css @@ -15,7 +15,7 @@ .theater-bg { - height: 100%; + height: inherit; width:100%; background-size: cover; background-image: url('../../img/background.webp'); diff --git a/src/components/subcomponents/Listing.js b/src/components/subcomponents/Listing.js index a9e7379..6fb3bb1 100644 --- a/src/components/subcomponents/Listing.js +++ b/src/components/subcomponents/Listing.js @@ -1,4 +1,5 @@ import React from 'react'; +import { Link } from 'react-router-dom'; import '../css/Listing.css'; const Listing = (props) => { @@ -11,12 +12,12 @@ const Listing = (props) => { */ return ( ); }; diff --git a/src/reducers/index.js b/src/reducers/index.js index 00bd921..6507fc9 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -3,7 +3,6 @@ import { combineReducers } from "redux"; import githubReducer from './githubReducer'; import contactModalReducer from "./contactModalReducer"; import introReducer from "./introReducer"; -import navigationReducer from "./navigationReducer"; import articlesReducer from "./articlesReducer"; import modelReducer from "./modelReducer"; @@ -11,7 +10,6 @@ export default combineReducers({ github: githubReducer, contactModal: contactModalReducer, intro: introReducer, - navigation: navigationReducer, articles: articlesReducer, model: modelReducer, }); diff --git a/src/reducers/navigationReducer.js b/src/reducers/navigationReducer.js deleted file mode 100644 index 44d22df..0000000 --- a/src/reducers/navigationReducer.js +++ /dev/null @@ -1,11 +0,0 @@ -let navigationReducer = (state={enable: true}, action) => { - switch(action.type) { - case 'SET_NAVIGATION': - return { ...state, enable: action.payload }; - default: - return state; - } - }; - - -export default navigationReducer; \ No newline at end of file