This shows you the differences between two versions of the page.
isc:labs:09 [2023/12/10 17:41] florin.stancu |
isc:labs:09 [2024/12/02 22:07] (current) dimitrie.valu |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | /* ~~SHOWSOLUTION~~ */ | + | ====== Lab 09 - Public Key Infrastructure ====== |
- | ====== Lab 09 - Web Security ====== | + | Remember the Cryptography lab? We're going to build on top of that by teaching you the basics of Public Key Infrastructure (PKI) and Transport Layer Security (TLS) shenanigans, giving you an overview of how they are integrated into your daily life, ensuring the security of your communications. |
- | ===== Objectives ===== | + | To start off, ask yourself the following (which is a common interview question for security-adjacent jobs, so you might want to remember it) - what would the world look like without cryptography? Don't worry - we'll get back to this question! |
- | * Web vulnerabilities, both server-side and client-side | + | |
- | * Server-side SQL injection | + | |
- | * Cross-Site Scripting, Cross-Site Request Forgery | + | |
- | ===== Background ===== | + | ===== Tasks ===== |
- | ==== SQL Injection ==== | + | For this section, open a text file and copy-paste/type into it the required information. You will use it to both cross-compare information and to provide proof of solving. |
- | SQL Injection is a server-side code injection vulnerability resulting from | + | Note: the files you create will be used throughout the lab, so keep them handy unless stated otherwise. If you <del>delete</del> lose them, no problem, just create them again. |
- | improper (unsanitized) input directly concatenated into SQL queries. | + | |
- | Typical server queries are built as strings: | + | ==== [5p] 0. Init ==== |
- | <code javascript> | + | |
- | sql = "SELECT * FROM table WHERE item = '" + user_input_variable + "' <other expressions>"; | + | You've learned about public and private keys in the Cryptography lab, and you learned to generate private and public keys during the OpenStack setup. We will go a bit more in-depth - run the following command to generate a private key: |
- | database.query(sql); | + | |
+ | <code bash> | ||
+ | openssl genrsa -out private.pem 2048 | ||
</code> | </code> | ||
- | Note that the user may choose to escape the SQL quotes and alter the SQL statement, e.g.: | + | If you run ''%%cat private.pem%%'', you will see how the private key is stored in PEM format. PEM is a base64-encoded format that is used to store cryptographic keys and certificates; there are [[https://myarch.com/public-private-key-file-formats |other formats as well]], however PEM is the most common one for X.509 certificates, CSRs, and cryptographic keys. |
- | <code javascript> | + | |
- | user_input_variable = "' OR 1=1 -- "; // example input given by the user | + | Now, generate a public key from the private key: |
- | sql = "SELECT * FROM table WHERE item = '' OR 1=1 -- ' <other expressions>"; | + | |
+ | <code bash> | ||
+ | openssl rsa -in private.pem -pubout -out public.pem | ||
</code> | </code> | ||
- | An SQL injection exploit ultimately depends on the target SQL expression (which | + | As you already know, you can sign a message with your private key and verify it with your public key. To do that, create a text file with a message of your choice and sign it with your private key: |
- | is usually unknown to the attacker) and query result behavior (whether the | + | |
- | query contents arem displayed on screen or the user is blind, errors reported | + | |
- | etc.). | + | |
- | **Make sure to check those cheatsheets out:** \\ | + | <code bash> |
- | [[https://portswigger.net/web-security/sql-injection]] \\ | + | echo "n1fl0_574nku" > message.txt |
- | [[https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/SQL%20Injection]] \\ | + | openssl dgst -sha256 -sign private.pem -out signature.sha256 message.txt |
- | and:\\ | + | </code> |
- | [[https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/SQL%20Injection/MySQL%20Injection.md]] | + | |
- | ==== Other server-side vulnerabilities ==== | + | To verify the signature, use the following command: |
- | The SQL injection is a popular server-side code injection vulnerability, but | + | <code bash> |
- | there are many mistakes that a website developer / system administrator can | + | openssl dgst -sha256 -verify public.pem -signature signature.sha256 message.txt |
- | make (//expect to find some of them in your homework :P //): | + | </code> |
- | * code injection (LDAP, eval, shell execution etc.); | + | The output should be: |
- | * broken authentication or access control (authorization); | + | |
- | * sensitive data exposure (e.g., backups / keys forgotten inside web root); | + | |
- | * path traversal; | + | |
- | * server misconfiguration; | + | |
- | * //[[https://owasp.org/www-community/vulnerabilities/|and many more]]// | + | |
- | There are even | + | <code> |
- | [[https://owasp.org/www-community/Vulnerability_Scanning_Tools|free or | + | Verified OK |
- | commercial web vulnerability scanners]] for testing a server's sercurity! | + | </code> |
+ | In practice, the private key is obviously kept secret ("private"), and the public key is shared publicly. This allows anyone to verify the authenticity of a message signed with the private key, and it lays a foundation for secure communication. Of course, this alone is not enough to provide security, as a man-in-the-middle attack could intercept the public key and replace it with a malicious one. This is where **Public Key Infrastructure (PKI)** comes into play. | ||
- | ==== Client-side vulnerabilities ==== | + | PKI is a convention that binds public keys to entities through the use of a **Certificate Authority (CA)**. A CA is a **trusted entity** that issues **digital certificates**, which are used to verify the identity of an entity. The CA signs the certificate with its private key, and the entity can then use the certificate to prove its identity. This creates a **chain of trust** that functions as follows: |
- | Browsers are now among the most targeted pieces of software on the Internet. | + | {{ :isc:labs:chain-of-trust.png?nolink&400 |}} |
- | This is mainly because of the large threat vector resulting from the complexity of | + | |
- | the web ecosystem, requiring features such as fancy HTML+CSS rendering, | + | |
- | animation and even sandboxed, untrusted JavaScript code execution. | + | |
- | Even when the browsers do a good job at protecting against attacks, sometimes | + | This means that you can manually check every signature up until (and including) the **Root CA**'s, which is the original certificate that is self-signed. This is the certificate that is used to sign other certificates, and it is the one that is actually vulnerable to attacks. Type ''%%mpr3073454%%'' on the first line of your text file to prove that you've completed this section (and then ignore this sentence). If the Root CA's private key is compromised, the entire chain of trust is compromised. Hence, **the level of trust increases the higher you go up the chain**: |
- | trusted websites themselved may contain bugs that directly affect the security | + | |
- | of their users. | + | |
- | A major threat, **Cross Site Scripting (XSS)** is a JavaScript code injection | + | {{ :isc:labs:pki.png?nolink&400 |}} |
- | vulnerability where an attacker that found a way to post public HTML scripts | + | |
- | into an unprotected website (e.g., by using comments forms or forum responses). | + | |
- | Those scripts, if served to other visitors, will execute with the credentials | + | |
- | of their respective users, making it possible for the attacker to scam, | + | |
- | exfiltrate personal data or even push malware into the victim's PC. | + | |
- | Another typical client-side vulnerability that the web developers need to | + | <ifauth @isc> |
- | protect their websites against is | + | <hidden> |
- | **[[https://research.securitum.com/what-is-the-csrf-cross-site-request-forgery-vulnerability/|Cross-Site | + | |
- | Request Forgery (CSRF)]]**. | + | |
- | In this attack, the victim is tricked into opening an attacker-controlled web | + | |
- | page which then issues custom requests (either using concealed elements that do | + | |
- | external requests - ''img'', ''form'', or by using JavaScript / AJAX) to | + | |
- | another (target) website. The browser will happily make the requests using the | + | |
- | target domain's cookies and credentials. | + | |
- | If the target website has URLs that execute certain actions (e.g., ''POST | + | |
- | https://my-blog/post.php'') without verifying the source of the request, any | + | |
- | malicious page can execute them. | + | |
- | Note that the attacker cannot see the results of those requests (unless | + | |
- | authorized by CORS headers by the target). | + | |
- | In practice, any URL endpoint executing sensitive actions needs to be protected | + | |
- | using either referer validation or CSRF tokens. | + | |
- | ===== Setup ===== | + | ''%%mpr3073454%%'' should be the first line of the text file if you've completed the section. |
- | You will be using a [[https://cloud.grid.pub.ro/|OpenStack VM]] for | + | </hidden> |
- | your tasks. | + | </ifauth> |
- | Remember that the instances have private IPs, ''10.9.x.y'', inaccessible from | ||
- | anywhere but the campus network. Since we need to use a local browser to access | ||
- | a web server running inside the VM, we will employ a SSH tunnelling + proxy | ||
- | trick to accomplish this. | ||
- | You should already have a [[:isc:info:virtualmachine|SSH keypair for authenticating with fep & OpenStack]]: | + | ==== [15p] 1. Construct ==== |
- | We will be using ''ssh''`s Local Port Forwarding feature, requesting it to pass all packets from ''localhost:8080'' through the SSH tunnel to the destination VM on ''8080'': | + | Now that you have seen how to generate a keypair and use it to sign and verify a message from the command line, we will delve a bit deeper and do it programatically. We will construct an RSA key manually, export the private and public keys into files, sign a message, and verify the signature. |
+ | |||
+ | Install the PyCryptodome library by running ''%%pip install pycryptodome%%'' and solve the TODOs below. Make sure to save the output of the public key in your text file. | ||
+ | |||
+ | <note tip> | ||
+ | For RSA key generation, check out [[https://en.wikipedia.org/wiki/RSA_(cryptosystem)#Key_generation |this Wikipedia article]] and [[https://asecuritysite.com/rsa/rsa |Bill Buchanan's tutorial]]. | ||
+ | |||
+ | For PyCryptodome's RSA API, check out the [[https://pycryptodome.readthedocs.io/en/latest/src/public_key/rsa.html |relevant documentation]]. | ||
+ | |||
+ | For signing and verifying messages, check out the relevant [[https://pycryptodome.readthedocs.io/en/latest/src/signature/pkcs1_v1_5.html |PyCryptodome documentation]]. | ||
+ | </note> | ||
+ | |||
+ | <code python> | ||
+ | from Crypto.PublicKey import RSA | ||
+ | from Crypto.Signature import pkcs1_15 | ||
+ | from Crypto.Hash import SHA256 | ||
+ | |||
+ | # e is the public exponent, it generally is 65537 by convention. | ||
+ | e = 65537 | ||
+ | |||
+ | # p and q are our secret two large prime numbers that are used in the generation of the RSA key. | ||
+ | p=10419935087756538088231008384871562040994908014661490082254127473816665841052170054492993091501556530959730347212080475120972015619902594355421427060481753 | ||
+ | q=11407307180430314273628852150740394587698189511945724632696575057978981037275333897647756309351469051358504590035493592824013771486348241061659599880138241 | ||
+ | |||
+ | # TODO 1: Compute N, phi, and d, and construct your RSA key, starting from the given p, q, and e. | ||
+ | ... | ||
+ | |||
+ | # TODO 2: Print the private and public keys in PEM format, then save them into a file each. | ||
+ | # Add the public key's output to your text file. | ||
+ | ... | ||
+ | |||
+ | # TODO 3: Concatenate, hash and sign the following two messages using your private key. | ||
+ | # This ensures that the signature is unique to the two messages, so the receiver can | ||
+ | # authenticate the sender. This is usually done over a network, and the values signed are | ||
+ | # concatenated Diffie Hellman keys, or a hash of the message and a timestamp. | ||
+ | message1 = b"sal" | ||
+ | message2 = b"boss" | ||
+ | ... | ||
+ | |||
+ | # TODO 4: Print the signature in hexadecimal format. | ||
+ | ... | ||
+ | |||
+ | # TODO 5: Verify the signature using the public key. | ||
+ | ... | ||
+ | </code> | ||
+ | |||
+ | <ifauth @isc> | ||
+ | <hidden> | ||
+ | <code python> | ||
+ | from Crypto.PublicKey import RSA | ||
+ | from Crypto.Signature import pkcs1_15 | ||
+ | from Crypto.Hash import SHA256 | ||
+ | |||
+ | # e is the public exponent, it generally is 65537 by convention. | ||
+ | e = 65537 | ||
+ | |||
+ | # p and q are our secret two large prime numbers that are used in the generation of the RSA key. | ||
+ | p=10419935087756538088231008384871562040994908014661490082254127473816665841052170054492993091501556530959730347212080475120972015619902594355421427060481753 | ||
+ | q=11407307180430314273628852150740394587698189511945724632696575057978981037275333897647756309351469051358504590035493592824013771486348241061659599880138241 | ||
+ | |||
+ | # TODO 1: Compute N, phi, and d, and construct your RSA key, starting from the given p, q, and e. | ||
+ | N = p * q | ||
+ | phi = (p-1)*(q-1) | ||
+ | d = pow(e, -1, phi) | ||
+ | rsa_key = RSA.construct((N, e, d)) | ||
+ | |||
+ | # TODO 2: Print the private and public keys in PEM format, then save them into a file each. | ||
+ | # Add the public key's output to your text file. | ||
+ | print(f"{rsa_key.export_key().decode()}\n\n{rsa_key.publickey().export_key().decode()}\n") | ||
+ | |||
+ | with open("key_private.pem", "wb") as f: | ||
+ | f.write(rsa_key.export_key()) | ||
+ | print("Private key saved to key_private.pem") | ||
+ | with open("key_public.pem", "wb") as f: | ||
+ | f.write(rsa_key.publickey().export_key()) | ||
+ | print("Public key saved to key_public.pem\n") | ||
+ | |||
+ | # TODO 3: Concatenate, hash and sign the following two messages using your private key. | ||
+ | # This ensures that the signature is unique to the two messages, so the receiver can | ||
+ | # authenticate the sender. This is usually done over a network, and the values signed are | ||
+ | # concatenated Diffie Hellman keys, or a hash of the message and a timestamp. | ||
+ | message1 = b"sal" | ||
+ | message2 = b"boss" | ||
+ | message = message1 + message2 | ||
+ | |||
+ | hash1 = SHA256.new(message) | ||
+ | signature = pkcs1_15.new(rsa_key).sign(hash1) | ||
+ | |||
+ | # TODO 4: Print the signature in hexadecimal format. | ||
+ | print(signature.hex()) | ||
+ | |||
+ | # TODO 5: Verify the signature using the public key. | ||
+ | try: | ||
+ | verifier = pkcs1_15.new(rsa_key.publickey()) | ||
+ | verifier.verify(hash1, signature) | ||
+ | print("Signature is valid") | ||
+ | except (ValueError, TypeError): | ||
+ | print("Signature is invalid") | ||
+ | </code> | ||
+ | </hidden> | ||
+ | </ifauth> | ||
+ | |||
+ | ==== [30p] 2. Investigate ==== | ||
+ | |||
+ | === [15p] Remote certificates === | ||
+ | |||
+ | Pick any website of your choice that uses HTTPS. | ||
+ | |||
+ | == [5p] From your browser == | ||
+ | |||
+ | Contemporary browsers come with a feature that allows you to check a website's certificates. It is found on the left side of the browser, under the shape of a lock. | ||
+ | |||
+ | Employ your browser’s certificate-viewing function to find information about the certificate of your chosen website. Copy the following attributes: | ||
+ | |||
+ | * Issuer's Common Name | ||
+ | * Subject's Common Name and Organization | ||
+ | * Validity dates (Not Before/Not After) | ||
+ | * Public key exponent | ||
+ | |||
+ | Save the information in the text file. Now count the number of certificates in the certificate chain and save that in the file too. | ||
+ | |||
+ | == [10p] From the terminal, like a h4xx0r == | ||
+ | |||
+ | Use the ''%%openssl%%'' command to find the same information about the same website. Use the following command to extract the end-entity certificate: | ||
<code bash> | <code bash> | ||
- | ssh -L "8080:localhost:8080" -J <first.lastname>@fep.grid.pub.ro student@10.9.X.Y | + | openssl x509 -in <(echo | openssl s_client -connect <hostname>:443 2>/dev/null | openssl x509 -text) -text -noout |
</code> | </code> | ||
- | ===== Tasks ===== | + | <note tip> |
+ | Why port 443? Because it is, by convention, reserved for HTTPS traffic. | ||
+ | </note> | ||
- | === 1 [20p]. SQL Injection === | + | To extract information about the certificate chain, use the following command: |
- | * Start the web server by using the following sequence: <code bash> | + | <code bash> |
- | # First, start the MySQL instance in background | + | true | openssl s_client -connect <hostname>:443 |
- | docker run -d --rm --name mysql ropubisc/lab08-mysql | + | </code> |
- | # Wait until the MySQL server fully starts: | + | |
- | docker logs mysql -f | + | |
- | # Ctrl+C and continue when it says: 'mysqld: ready for connections.' | + | |
- | # Finally, start the sample web server | + | Compare the information you found in the browser with the information you found in the terminal. Is there any difference? Write down the answer in the text file. |
- | docker run -it --link mysql:mysql -p 8080:8080 ropubisc/lab08-web-server | + | |
+ | <ifauth @isc> | ||
+ | <hidden> | ||
+ | |||
+ | They're the same. | ||
+ | |||
+ | </hidden> | ||
+ | </ifauth> | ||
+ | |||
+ | === [15p] Local certificates === | ||
+ | |||
+ | Pick another website. Proceed to download the certificate using the following command: | ||
+ | |||
+ | <code bash> | ||
+ | true | openssl s_client -connect <hostname>:443 2>/dev/null | openssl x509 > <filename>.crt | ||
</code> | </code> | ||
- | * Connect to the application using [[http://localhost:8080/]] (assuming you forwarded the port correctly) | + | |
- | * Now: You don't know any user / password for this website. Try to log in using SQL Injection! | + | You can now display information from the locally downloaded file using the following command: |
- | * The most common approach when testing for SQL Injection is to input an apostrophe ("'") in any of the provided fields ([[https://security.stackexchange.com/questions/67972/why-do-testers-often-use-the-single-quote-to-test-for-sql-injection]]) | + | |
- | * Hint: Try the apostrophe in one of the login fields, check if it shows an error! | + | <code bash> |
- | * Note: After examining the error the form prompts, we can assume how the query is being made: <code sql> | + | openssl x509 -in <filename>.crt -text -noout |
- | 'SELECT <some columns> FROM users WHERE username = ' + username + ' AND password = ' + password + ' LIMIT 1;' | + | |
</code> | </code> | ||
- | * Is pretty obvious that the strings provided are not escaped and we can abuse this misconfiguration. Check the links in the beginning! | + | |
- | * If you ever want to exit the MySQL server:<code> | + | Within 4 commands, find the arguments you need to display the following 4 attributes in the command line (hint: ''%%man openssl-x509%%'' and search for each one using ''%%/%%'' followed by the desired term): |
- | docker kill mysql | + | |
+ | * Issuer | ||
+ | * Subject | ||
+ | * Validity dates | ||
+ | * Public key | ||
+ | |||
+ | Save the last 2 commands in the text file. You may then delete the downloaded certificate if you so desire (we won't need it anymore). | ||
+ | |||
+ | <ifauth @isc> | ||
+ | <hidden> | ||
+ | |||
+ | You need to replace -text with the desired parameter. The commands will look like the following: | ||
+ | |||
+ | <code bash> | ||
+ | openssl x509 -in <filename>.crt -issuer -noout | ||
+ | openssl x509 -in <filename>.crt -subject -noout | ||
+ | openssl x509 -in <filename>.crt -dates -noout | ||
+ | openssl x509 -in <filename>.crt -pubkey -noout | ||
</code> | </code> | ||
- | <solution -hidden> | + | The students should have the last 2 commands saved in the text file. |
- | Login with ''%%' OR 1=1 -- -%%'' | + | |
- | </solution> | + | |
- | === 2 [20p]. Advanced SQL Injection === | + | </hidden> |
+ | </ifauth> | ||
- | * Start the web server from the first task again. | + | ==== [20p] 3. Create ==== |
- | * What if I told you there is a hidden **flag** inside the database? Find it! | + | |
- | * Hint: where do you have query feedback inside the application? try to do an ''UNION'' hack! | + | |
- | * Note: Since we will be using the same query as the one used in the first exercise, we must first find the exact number of columns provided to the statement. We are aiming at building a query of this format: <code sql> | + | |
- | SELECT col1, col2, ..., colN from users WHERE username ='' UNION SELECT col1, col2, ... , colN-1, desired_column from desired_database_table --;</code> | + | |
- | * Note: for UNION to work, you must SELECT exactly the same number of columns as in the original query! | + | |
- | * After finding out the exact number of columns, we can use ''GROUP_CONCAT'' technique to extract the available database table names.\\ Check out the cheatsheets from the Background section! (P.S. database schema is ''journalapp'') | + | |
- | * Hint: <code>UNION SELECT col1, col2, ... , colN-1, GROUP_CONCAT(<what are we looking for in the schema>) FROM information_schema.tables WHERE table_schema='<our schema name>' </code> \\ It is not necessary to know the exact names of 'col1', 'col2', ... 'colN-1'. You can replace it with numbers or '@'. | + | |
- | * Got any table that catches your eye? We are going to use ''GROUP_CONCAT'' again, but this time we are trying to find the name of the columns of our desired table. | + | |
- | * Hint: <code>UNION SELECT col1, col2, ... , colN-1, GROUP_CONCAT(<what are we looking for in the table>) FROM information_schema.columns WHERE table_name='<our table name>' </code> | + | |
- | * Got the column name? Good. Now it should be nothing more than a simple select query :) | + | |
- | * Hint: <code>UNION SELECT col1, col2, ... , colN-1, <desired column name> FROM <desired table name></code> | + | |
- | <solution -hidden> | + | Now that you've familiarized yourself with how certificates look, let's create our own Certificate Authority (CA) and sign a certificate with it. We will use the private key you generated in the first task. |
- | Find the number of main table columns: ''%%asd' UNION (SELECT 1, 2, 3, 4) -- .%%''\\ | + | |
- | Find the tables: ''%%' UNION SELECT 1, 2, 3, GROUP_CONCAT(table_name) FROM information_schema.tables WHERE table_schema='journalapp' -- .%%''\\ | + | |
- | Find the columns: ''%%' UNION SELECT 1, 2, 3, GROUP_CONCAT(column_name) FROM information_schema.columns WHERE table_name='flags' -- .%%''\\ | + | |
- | Find the flag: ''%%' UNION SELECT 1, 2, 3, flag FROM flags -- .%%''\\ | + | |
- | </solution> | + | |
- | === 3 [20p]. Cross-Site Scripting === | + | === [10p] Create a CA === |
- | * You can still save the day: cover the monster's mouth (you can use the image at ''http://localhost:8080/images/muzzle.png'')! | + | Create a self-signed certificate using the following command: |
- | * Since HTML is allowed, you can also inject a JavaScript alert, example:<code> | + | |
- | <script>alert("XSS!");</script> | + | <code bash> |
+ | openssl req -new -x509 -key private.pem -out ca.pem -days 365 | ||
</code> | </code> | ||
- | * **Hint**: You can use absolute element positioning, e.g.: ''<div style="position: absolute; top: -300px;left:100px;"> insert your img here </div>''. Try it with the browser's developer console / inspect element first before injecting it inside a message ;) \\ [[https://www.w3schools.com/css/css_positioning.asp]] | ||
- | * Hint: Console can be accessed either by right-clicking and choosing Inspect or by hitting F12 on your keyboard and navigating to 'Console' tab. | ||
- | * Note: Try appending code to the document's body. | ||
- | <solution -hidden> | + | This command generates a new certificate signing request (CSR) and a new self-signed certificate. The ''%%-x509%%'' option specifies that the output is a self-signed certificate. The ''%%-days%%'' option specifies the number of days the certificate is valid for. You can view the contents of the certificate using the following command: |
- | <code> | + | |
- | <div style="position: absolute; top: -510px; left: 250px"><img src="/images/muzzle.png"></div> | + | <code bash> |
- | <script>alert("No more Shosho, phew!");</script> | + | openssl x509 -in ca.pem -text -noout |
</code> | </code> | ||
- | </solution> | ||
- | === 4 [20p]. Cross-Site Request Forgery === | + | What are the differences between the certificate you generated and the one you extracted from a website? Write down the answer in the text file. |
+ | |||
+ | <ifauth @isc> | ||
+ | <hidden> | ||
+ | |||
+ | The certificate you generated is self-signed, while the one you extracted from a website is signed by a Certificate Authority. | ||
+ | |||
+ | </hidden> | ||
+ | </ifauth> | ||
+ | |||
+ | === [10p] Sign a certificate === | ||
+ | |||
+ | Now that you have a CA, you can sign a certificate with it. Generate a certificate signing request (CSR) using the following command: | ||
+ | |||
+ | <code bash> | ||
+ | openssl req -new -key private.pem -out request.csr | ||
+ | </code> | ||
+ | |||
+ | The CSR contains information about the entity that is requesting the certificate, such as the Common Name (CN), Organization, and Location. You can view the contents of the CSR using the following command: | ||
+ | |||
+ | <code bash> | ||
+ | openssl req -in request.csr -text -noout | ||
+ | </code> | ||
+ | |||
+ | This signing request is meant to be sent to a Certificate Authority, which will then sign it with its private key. However, we will act as our own CA and sign the certificate ourselves. Sign the CSR with your private key to create a certificate: | ||
+ | |||
+ | <code bash> | ||
+ | openssl x509 -req -in request.csr -CA ca.pem -CAkey private.pem -CAcreateserial -out cert.pem -days 365 | ||
+ | </code> | ||
+ | |||
+ | You can view the contents of the certificate using the following command: | ||
+ | |||
+ | <code bash> | ||
+ | openssl x509 -in cert.pem -text -noout | ||
+ | </code> | ||
+ | |||
+ | Now, take {{ :isc:labs:isc_request.txt |this CSR}} (the extension is ''%%.txt%%'' to bypass OCW restrictions :)) ) and sign it using your CA. Display the signed certificate's contents and save the output in your text file. | ||
+ | |||
+ | <ifauth @isc> | ||
+ | <hidden> | ||
+ | |||
+ | The command to sign the CSR is the following: | ||
+ | |||
+ | <code bash> | ||
+ | openssl x509 -req -in isc_request.txt -CA ca.pem -CAkey private.pem -CAcreateserial -out cert.pem -days 365 | ||
+ | </code> | ||
- | * The objective is to fool your victim using an external website (simulated using a local .html page) to post an attacker-controller message into the website. | + | As a result, you can check for the following subject field in the text file: |
- | * On your local station, as the attacker: create a simple HTML page that posts a hidden message to [[http://localhost:8080/journal/post]]; this will be equivalent to hosting a malicious website; | + | |
- | * Hint: Check the HTML of the website for a ''%%<form>%%'' example! | + | |
- | * Hint: Use an input with ''%%type="hidden"%%'' | + | |
- | * Switching sides (you're the victim, now): open that HTML page using your web browser. Click a button and the hidden message will be posted ;) | + | |
- | * (Make sure you are logged in inside the web app before doing the attack!) | + | |
- | * For bonus, you can try to do a cross-site AJAX posting the message automatically (without requiring user interaction!). | + | |
- | <solution -hidden> | ||
<code> | <code> | ||
- | <form action="http://localhost:8080/journal/post" method="post"> | + | Subject: C = RO, ST = Bucharest, L = Bucharest, O = UNSTPB, |
- | <input type="hidden" name="message" value="I am stupid!"> | + | OU = "ACS, CTI", CN = Introducere in Securitate Cibernetica |
- | <input type="submit" value="Click me!"> | + | |
- | </form> | + | |
</code> | </code> | ||
- | </solution> | ||
- | === 5 [20p]. Server Reconnaissance === | + | </hidden> |
+ | </ifauth> | ||
- | * Can you steal the source code of the server-side code using HTTP only? | ||
- | * Once you found it, try to find the database credentials! | ||
- | * Hint: try to guess the path to a [[https://docs.npmjs.com/files/package.json|common file]] that all NodeJS projects have! It may reference the main script's name! | ||
- | * Also try it by using a tool: [[https://cirt.net/nikto2|nikto]], ''apt install nikto'' | ||
- | <solution -hidden> | + | ==== [30p] 4. Develop ==== |
+ | |||
+ | Run ''%%pip install flask%%'' to install Flask, a Python web framework that we will use to implement a simple web server over both HTTP and HTTPS. You will most likely use it or an alternative in the future, so it's good to get acquainted with it - it's very accessible for prototyping and running your own infrastructure. | ||
+ | |||
+ | Create a Python script that runs a simple web server over HTTP: | ||
+ | |||
+ | <code python> | ||
+ | from flask import Flask | ||
+ | |||
+ | app = Flask(__name__) | ||
+ | |||
+ | @app.route("/") | ||
+ | def hello(): | ||
+ | return "Hello World!" | ||
+ | |||
+ | if __name__ == "__main__": | ||
+ | app.run(port=1337) | ||
+ | </code> | ||
+ | |||
+ | <note important> | ||
+ | Don't name your script ''%%flask.py%%'' - Flask will try to import itself, and you will get an error (circular import). It is fairly common to name a Flask webapp ''%%app.py%%''. | ||
+ | </note> | ||
+ | |||
+ | <note tip> | ||
+ | By convention, port 80 is reserved for HTTP traffic, and port 443 is reserved for HTTPS; we're using port 1337 here to avoid conflicts with other services that might be running on your machine. | ||
+ | </note> | ||
+ | |||
+ | === [15p] HTTP vs HTTPS === | ||
+ | |||
+ | Run ''%%curl http://127.0.0.1:1337%%'' in another terminal. What do you see? What happens if you run ''%%curl https://127.0.0.1:1337%%'' (changed ''%%http%%'' to ''%%https%%'') and why? | ||
+ | |||
+ | With the last command, you will get an output similar to the following: | ||
<code> | <code> | ||
- | GET /package.json | + | 127.0.0.1 - - [05/Jul/1337 16:82:98] "\x16\x03\x01 [...]" HTTPStatus.BAD_REQUEST - |
- | it references the main file: | + | |
- | GET /server.js | + | |
</code> | </code> | ||
- | </solution> | ||
- | === Feedback === | + | This happens because the server is set up for HTTP only, but ''%%curl%%'' is initiating a TLS handshake by sending the Client Hello. TLS is a protocol that provides privacy and data integrity between two communicating applications. It's the most widely used protocol for securing communication over the Internet, and it's the protocol that powers HTTPS. |
+ | |||
+ | {{ :isc:labs:tls.png?nolink&400 |}} | ||
+ | |||
+ | Take a look at [[https://tls12.xargs.org/ |this]] and identify what the first 5 bytes within the server's mangled output signify, only using the information from that website - no need to go more in-depth. Note what you found into your text file. | ||
+ | |||
+ | <ifauth @isc> | ||
+ | <hidden> | ||
+ | |||
+ | The bytes are part of the TLS Client Hello's Record Header - specifically, ''%%0x16%%'' signifies the handshake protocol, ''%%0x03 0x01%%'' translate to the TLS version, and ''%%0x02 0x00%%'' represent the cipher suites. | ||
+ | |||
+ | </hidden> | ||
+ | </ifauth> | ||
+ | |||
+ | === [15p] HTTPS server === | ||
+ | |||
+ | Download {{ :isc:labs:isc_lab09.zip |this .zip file}} and extract its contents. There, you will find multiple files; for now, try and verify if the ''%%isc_signed_cert.crt%%'' is valid using the following command: | ||
+ | |||
+ | <code bash> | ||
+ | openssl verify isc_signed_cert.crt | ||
+ | </code> | ||
+ | |||
+ | Why did it fail? OpenSSL references a default directory containing trusted CA certificates when verifying a certificate. Copy ''%%isc_ca.crt%%'' to ''%%/usr/local/share/ca-certificates%%'' and run the following command to update the CA certificates: | ||
+ | |||
+ | <code bash> | ||
+ | update-ca-certificates | ||
+ | </code> | ||
+ | |||
+ | Now verify the certificate again. Has the output changed? Save the output in your text file. | ||
+ | |||
+ | You can also manually specify a CA file for verification: | ||
+ | |||
+ | <code bash> | ||
+ | openssl verify -CAfile isc_ca.crt isc_signed_cert.crt | ||
+ | </code> | ||
+ | |||
+ | Modify your Python script to run the server over HTTPS. To do that, add the ''%%ssl_context%%'' argument to the ''%%app.run%%'' function call, with the following parameters: | ||
+ | |||
+ | * ''%%certfile%%'' - the path to the client's signed certificate (''%%isc_signed_cert.crt%%'') | ||
+ | * ''%%keyfile%%'' - the path to the client's private key file (''%%client_key.pem%%'') | ||
+ | |||
+ | Now try to run ''%%curl http://127.0.0.1:1337%%''. What do you see? Find a way to run ''%%curl%%'' on the HTTPS server - first, change the URL to start with the correct protocol. Now what happens? Find a way to overcome this issue by using a specific ''%%curl%%'' command flag. Note the command into your text file. | ||
+ | |||
+ | <ifauth @isc> | ||
+ | <hidden> | ||
+ | |||
+ | You need to modify the last line in the script to look like this: | ||
+ | |||
+ | <code python> | ||
+ | app.run(ssl_context=('isc_signed_cert.crt', 'client_key.pem'), port=1337) | ||
+ | </code> | ||
+ | |||
+ | And then modify it to look like this: | ||
+ | |||
+ | <code python> | ||
+ | app.run(ssl_context=('correct_signed_cert.crt', 'client_key.pem'), port=1337) | ||
+ | </code> | ||
+ | |||
+ | You need to use ''%%--insecure%%'' or ''%%-k%%'' to ignore certificate validation: | ||
+ | |||
+ | <code bash> | ||
+ | curl -k https://127.0.0.1:1337 | ||
+ | curl --insecure https://127.0.0.1:1337 | ||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | </ifauth> | ||
+ | |||
+ | Instead of bypassing the check, let's use a certificate that is set up for us. Take a look at the ''%%correct_signed_cert.crt%%'' file - it is valid because it uses the correct domain name (''%%127.0.0.1%%'') as its Common Name. Modify the script to use it and start the server again. Now, try to run ''%%curl https://127.0.0.1:1337%%''. What do you see? Save the output in your text file. | ||
+ | |||
+ | <ifauth @isc> | ||
+ | <hidden> | ||
+ | |||
+ | This time, the ''%%curl%%'' command should successfully return ''%%Hello World!%%''. | ||
+ | |||
+ | </hidden> | ||
+ | </ifauth> | ||
+ | |||
+ | === [+rep] BONUS: traffic analysis === | ||
+ | |||
+ | Use ''%%tcpdump%%'' to capture traffic between a client and the server over both HTTP and HTTPS. Take a screenshot of the different outputs and use them as proof of solving. | ||
- | Please take a minute to fill in the [[https://forms.gle/5Lu1mFa63zptk2ox9|feedback form]] for this lab. | + | ==== 5. Terminate ==== |
+ | Now that you have wrapped your shell around the fundamentals of securing our daily communication, we've come full circle to the question... what would the world look like without cryptography? | ||