DoyenSec
const express = require("express");
// <4.19.2 & >=5.0.0-alpha.1 < 5.0.0-beta.3 : Vulnerable to Open Redirect
// <3.11.0 & >=4.0.0 < 4.5.0 : Vulnerable to XSS
// How to fix: Upgrade express to version 4.19.2, 5.0.0-beta.3 or higher
// Reference: https://security.snyk.io/package/npm/express
const app = express();
const escape = require('escape-html');
const url = require("url");
const adminSecret = process.env.ADMIN_SECRET;
function checkUrl(urlToCheck) {
try {
const {protocol, host} = url.parse(urlToCheck);
if (protocol !== "https:" || host !== "nc.bank")
return false;
return true;
} catch (err) {
return false;
}
}
app.get("/invoice", (req, res) => {
const id = req.query.id;
if (!id) {
return res.status(400).send('id is required');
}
const invoiceIdRegex = /^[a-f0-9]{64}$/;
const match = id.match(invoiceIdRegex);
const asExpected = match !== null;
if (!asExpected) {
return res.status(400).send('invalid invoice id');
}
let html = `
<!DOCTYPE html>
<head>
<title>Show invoice</title>
</head>
<body>
<p>Invoice search system is not working. Can't search for ` + id + `</p>
</body>
</html>`;
res.send(html);
});
app.get("/redirect", (req, res) => {
const to = req.query.to;
if (!to) {
return res.status(400).send('to is required');
}
if (checkUrl(to)) {
res.redirect(to)
}
else {
return res.status(400).send('Invalid URL');
}
});
// Vulnerability: Open Redirect
// Description: The application contains an open redirect vulnerability in the /redirect route, allowing attackers to redirect users to arbitrary websites outside of the intended domain.
// POC: node -e 'console.log(require("url").parse("https://nc.bank///@evil.com"))'
// Steps to reproduce:
// 1. Payload: https://<app-endpoint>/redirect?to=https://nc.bank///@evil.com
// 2. If nc.bank not properly configured for URL path handling, the '@' sign will redirect it to evil.com
// Impact: Attackers can exploit this vulnerability to trick users into visiting malicious websites
// Mitigation: Perform more comprehensive URL validation by validating the entire URL structure
app.get("/invoice/delete", (req, res) => {
const id = req.query.id;
if (!id) {
return res.status(400).send('id is required');
}
const invoiceIdRegex = /^[a-f0-9]{64}$/;
const match = id.match(invoiceIdRegex);
const asExpected = match !== null;
if (!asExpected) {
return res.status(400).send('invalid invoice id');
}
const secretKey = req.headers['admin_secret']
if (adminSecret !== secretKey) {
return res.status(401).send();
}
res.json({status: 'Success'})
});
app.get("/forms", (req, res) => {
const list = req.query.list;
if (!list) {
return res.status(400).send('list is required');
}
const array = list.split(',');
const on = req.query.on;
let html = `
<!DOCTYPE html>
<head>
<title>List of required forms</title>
</head>
<body>
<h3>Please fill the forms below:</h3>
<div>
<ul>`;
array.forEach((item) => {
const escapedItem = escape(item);
if (!on) {
html += `<li><a href="/${escapedItem}">${escapedItem}</a></li>`;
}
else {
html += `<li><a href="https://${on}/${escapedItem}">${escapedItem}</a></li>`;
}
});
html += `</ul>
</div>
</body>
</html>`;
res.send(html);
});
app.listen(1337, () => {
console.log("Server listening on port 1337");
});
Package Health:
1. <4.19.2 & >=5.0.0-alpha.1 < 5.0.0-beta.3 : Vulnerable to Open Redirect
2. <3.11.0 & >=4.0.0 < 4.5.0 : Vulnerable to XSS
3. How to fix: Upgrade express to version 4.19.2, 5.0.0-beta.3 or higher
4. Reference: https://security.snyk.io/package/npm/express
1. Vulnerability: Open Redirect
2. Description: The application contains an open redirect vulnerability in the /redirect route, allowing attackers to redirect users to arbitrary websites outside of the intended domain.
3. POC: node -e 'console.log(require("url").parse("https://nc.bank///@evil.com"))'
4. Steps to reproduce:
a. Payload: https://<app-endpoint>/redirect?to=https://nc.bank///@evil.com
b. If nc.bank not properly configured for URL path handling, the '@' sign will redirect it to evil.com
5. Impact: Attackers can exploit this vulnerability to trick users into visiting malicious websites
6. Mitigation: Perform more comprehensive URL validation by validating the entire URL structure
var express = require("express");
// <4.19.2 & >=5.0.0-alpha.1 < 5.0.0-beta.3 : Vulnerable to Open Redirect
// <3.11.0 & >=4.0.0 < 4.5.0 : Vulnerable to XSS
// How to fix: Upgrade express to version 4.19.2, 5.0.0-beta.3 or higher
// Reference: https://security.snyk.io/package/npm/express
var bodyParser = require("body-parser");
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var sqlite3 = require('sqlite3').verbose();
// >=5.0.0 <5.1.5 -> Vulnerable for Arbitary Code Execution
// <5.0.3 -> Vulnerable for DOS
// How to fix: Upgrade sqlite3 to version 5.1.5 or higher
// Refernece: https://security.snyk.io/package/npm/sqlite3
var db = new sqlite3.Database('mynotes.db');
db.serialize(function() {
// The database related code is encapsulated within the separate module
// Various database operation can ba encapsulated in the function
db.run("CREATE TABLE if not exists user (id INTEGER PRIMARY KEY,name TEXT)");
db.run("CREATE TABLE if not exists notes (id INTEGER PRIMARY KEY,content TEXT, user_id INTEGER, FOREIGN KEY(user_id) REFERENCES user(id))");
var stmt = db.prepare("INSERT INTO user (name) VALUES (?)");
for (var i = 0; i < 10; i++) {
stmt.run("Name1" + i);
}
stmt.finalize();
var stmt = db.prepare("INSERT INTO notes (content, user_id) VALUES (?,?)");
for (var i = 0; i < 10; i++) {
stmt.run("Note Content " + i, i+1);
}
stmt.finalize();
});
function isCurrentUser(user_id) {
// Vulnerability: Vulnerable to Session Hijacking if current_session token exposed in Cookies or Burp Suite Interception.
// Impact: If an attacker successfully steals the current_session token through cookie theft/Network sniffing, they can impersonate the user's session, gaining unauthorized access to their account.
// Mitigation: 1. Implement Secure Session Management (session encryption, secure storage, and protection against session fixation attacks)
// 2.Implement Session Expiry and Invalidation
// Verify that the current session belongs to the user specified as argument
// Return 'true' -> if current_session == user_id
// Return 'false' -> if current_session != user_id
}
app.get('/account/:user/:note', function(req, res) {
var user = req.params.user;
var note = req.params.note;
if(!isCurrentUser(user)){
res.end("Unauthorized!!!\n");
}
// Data type of 'note' is not validated
var search = db.get("SELECT * FROM notes where id = (?)", note, function(err, row) {
console.log(row);
console.log(err);
// Information Dislosure via Error Message CWE-209
res.send(row);
// Response not handled correctly (Cases: 1. Success(Note Exists) 2. Empty(Note doesn't exists) 3. Error)
});
});
app.listen(8081,function(){
console.log("Started on PORT 8081");
});
// Recommendation: Abstract operations into separate modules
// Modules: 1. Database module(db.js) 2. Auth module(auth.js) 3. Express Routes (app.js)
// This separation improves code maintainability, scalability, and security.
Package Health:
1. <4.19.2 & >=5.0.0-alpha.1 < 5.0.0-beta.3 : Vulnerable to Open Redirect
2. <3.11.0 & >=4.0.0 < 4.5.0 : Vulnerable to XSS
3. How to fix: Upgrade express to version 4.19.2, 5.0.0-beta.3 or higher
4. Reference: https://security.snyk.io/package/npm/express
Package Health:
1. >=5.0.0 <5.1.5 -> Vulnerable for Arbitary Code Execution
2. <5.0.3 -> Vulnerable for DOS
3. How to fix: Upgrade sqlite3 to version 5.1.5 or higher
4. Refernece: https://security.snyk.io/package/npm/sqlite3
1. Vulnerability: Vulnerable to Session Hijacking if current_session token exposed in Cookies or Burp Suite Interception.
2. Impact: If an attacker successfully steals the current_session token through cookie theft/Network sniffing, they can impersonate the user's session, gaining unauthorized access to their account.
3. Mitigation: a. Implement Secure Session Management (session encryption, secure storage, and protection against session fixation attacks)
b.Implement Session Expiry and Invalidation
1. Overall Recommendation: Abstract operations into separate modules
2. Modules: a. Database module(db.js) b. Auth module(auth.js) c. Express Routes (app.js)
3. This separation improves code maintainability, scalability, and security.
from flask import Flask, session, redirect, url_for, escape, request, render_template_string
app = Flask(__name__)
def search_term(term):
search_tags = ["twitter", "webpage", "inql", "electronegativity"]
print(term)
if any(st in term for st in search_tags):
if "electronegativity" :
return str(term) + ": https://github.com/doyensec/electronegativity"
if "twitter" in term:
return str(term) + ": https://twitter.com/doyensec"
if "webpage" in term:
return str(term) + ": https://www.doyensec.com/"
if "inql" in term:
return str(term) + ": https://github.com/doyensec/inql"
# Due to returning user input 'term', it is potentially vulnerable to injection attacks
@app.route('/')
def index():
search = request.args.get('search') or None
print(search)
if search is not None:
result = search_term(search)
else:
result = None
template = '''
<h1>Search for Doyensec in media</h1>
<p>Your search results:</p>
{}
'''.format(escape(result))
# Convert the characters &, <, >, โ, and โ in string s to HTML-safe sequences
# Reference: https://tedboy.github.io/flask/generated/flask.escape.html?highlight=escape
# Vulnerability: Server Side Template Injection (SSTI)
# POC: http://127.0.0.1:8888/?search=twitter'{{3*3}}
# Steps to reproduce:
# 1. Bypass search parameter with 'twitter<payload>'
# 2. search_term function will return a response
# 3. 'escape' function only converts &, <, >, โ, and โ to HTML-safe sequences, but it doesn't filter '{}' that makes it vulnerable for SSTI
# 4. SSTI can be detected using above POC.
# 5. SSTI can be escalated to RCE: https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection/jinja2-ssti
# 6. This can be chained to exploit XSS. HTML Encoding can be bypassed using 'safe': {{'<script>alert(1);</script>'|safe}} -> <script>alert(1);</script>
# Mitigation:
# 1. 'render_template_string' is common sink for SSTI
# 2. render_template_string() renders a Jinja2 template directly from a string. If the template is modified in any way, such as with string formatting, it creates a potential server-side template injection. Using render_template() is strictly safer because it does not create an opportunity to modify the template.
# Reference: https://semgrep.dev/docs/cheat-sheets/flask-xss/
return render_template_string(template)
@app.route('/doc')
def create_doc():
template = ''' <h1>Create doc file</h1>
<form method='post' name='f1' action='/view'>
<textarea name='doc' rows='10' cols='40'></textarea>
<br /><br />
<input type='submit'/>
</form>
'''
return render_template_string(template)
@app.route('/view',methods=['POST'])
def view_doc():
doc_string = request.form.get('doc')
template = '''
<h1>Here is your doc!</h1>
<br/>
<div style='border: solid 1px; width:200px; height:500px;'>
%s
</div>''' % (doc_string)
return template
# Vulnerability: Cross-Site Scripting (XSS)
# POC: Paylod - hello</div><script>alert('XSS attack!')</script>
# Steps to reproduce:
# 1. Submit a POST request to the /view endpoint & set as the value of the doc parameter to payload.
# 2. It will render string in repsonse & XSS will be triggered
# Mitigation:
# 1. Sanitize user inputs by escaping dangerous characters
print ('##################################################')
print ('http://127.0.0.1:8888/')
print ('##################################################')
app.run("0.0.0.0", 8888, app)
1. Convert the characters &, <, >, โ, and โ in string s to HTML-safe sequences
2. Reference: https://tedboy.github.io/flask/generated/flask.escape.html?highlight=escape
1. Vulnerability: Server Side Template Injection (SSTI)
2. POC: http://127.0.0.1:8888/?search=twitter'{{3*3}}
3. Steps to reproduce:
a. Bypass search parameter with 'twitter<payload>'
b. search_term function will return a response
c. 'escape' function only converts &, <, >, โ, and โ to HTML-safe sequences, but it doesn't filter '{}' that makes it vulnerable for SSTI
d. SSTI can be detected using above POC.
e. SSTI can be escalated to RCE: https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection/jinja2-ssti
f. This can be chained to exploit XSS. HTML Encoding can be bypassed using 'safe': {{'<script>alert(1);</script>'|safe}} -> <script>alert(1);</script>
4. Mitigation:
a. 'render_template_string' is common sink for SSTI
b. render_template_string() renders a Jinja2 template directly from a string. If the template is modified in any way, such as with string formatting, it creates a potential server-side template injection. Using render_template() is strictly safer because it does not create an opportunity to modify the template.
5. Reference: https://semgrep.dev/docs/cheat-sheets/flask-xss/
1. Vulnerability: Cross-Site Scripting (XSS)
2. POC: Paylod - hello</div><script>alert('XSS attack!')</script>
3. Steps to reproduce:
a. Submit a POST request to the /view endpoint & set as the value of the doc parameter to payload.
b. It will render string in repsonse & XSS will be triggered
4 Mitigation:
a. Sanitize user inputs by escaping dangerous characters
package main
import (
"regexp"
"github.com/gin-gonic/gin"
// <1.9.1 : Improper Input Validation
// <1.6.0 : Improper Output Neutralization for Logs
// <1.7.7 : HTTP Response Splitting
// <1.6.0 : Log Injection
// How to fix: Upgrade github.com/gin-gonic/gin to version 1.9.1 or higher.
// Reference: https://security.snyk.io/package/golang/github.com%2Fgin-gonic%2Fgin
)
var idRegex = regexp.MustCompile("^[a-f0-9]{64}$")
var itemRegex = regexp.MustCompile("^[a-zA-Z0-9]+$")
func main() {
r := gin.Default()
r.GET("/storage/check", func(c *gin.Context) {
items := map[string]int{
"jacket": 2,
"sunglasses": 2,
"sandevistan": 1,
}
c.JSON(200, items)
})
r.POST("/storage/send", func(c *gin.Context) {
// Assume authorization and authentication are properly managed
origin := c.GetHeader("Origin")
if origin == "" {
c.JSON(403, gin.H{"error": "Forbidden"})
return
}
r, _ := regexp.Compile(".*\\.nc.storage")
matchedString := r.MatchString(origin)
if matchedString {
c.Header("Access-Control-Allow-Origin", origin)
c.Header("Access-Control-Allow-Credentials", "true")
} else if origin == "null" {
c.Header("Access-Control-Allow-Origin", "null")
c.Header("Access-Control-Allow-Credentials", "true")
} else {
c.JSON(403, gin.H{"error": "Forbidden"})
return
}
// Vulnerability: Origin Header Injection
// Description: The application is vulnerable to an Origin Header Injection attack due to insufficient validation of the Origin header in CORS (Cross-Origin Resource Sharing) policy implementation.
// Payload: Origin: http://test.nc.storage.evil.com
// Step to reproduce:
// 1. Include the Origin header with a crafted value containing a subdomain ending with ".nc.storage" and a malicious domain, such as "test.nc.storage.evil.com".
// 2. Send POST request to '/storage/send'
// 3. The injected value "test.nc.storage.evil.com" matches the regex pattern and is considered a valid origin.
// 4. As a result, the server sets the Access-Control-Allow-Origin header to "test.nc.storage.evil.com" and allows the request to proceed. If the server responds with an HTTP status code indicating successful access (e.g., 200 OK), the vulnerability is confirmed.
// 5. This enables the attacker to bypass Cross-Origin Resource Sharing (CORS) restrictions and potentially execute unauthorized actions on behalf of the victim.
// Impact: The attacker can access sensitive endpoints or data intended only for trusted origins, leading to information disclosure or data manipulation.
// Mitigation: Update the regular expression used for validating the Origin header to enforce stricter validation criteria.
// Reference: https://portswigger.net/web-security/cors#how-to-prevent-cors-based-attacks
itemName := c.PostForm("item_name")
if itemName == "" {
c.JSON(400, gin.H{"error": "item_name required"})
return
}
if !itemRegex.MatchString(itemName) {
c.JSON(400, gin.H{"error": "invalid item_name"})
return
}
recipientID := c.PostForm("recipient_id")
if recipientID == "" {
c.JSON(400, gin.H{"error": "recipient_id required"})
return
}
if !idRegex.MatchString(recipientID) {
c.JSON(400, gin.H{"error": "invalid recipient_id"})
return
}
c.JSON(200, gin.H{"message": itemName + " sent successfully"})
})
r.Run(":1337")
}
<1.9.1 : Improper Input Validation
<1.6.0 : Improper Output Neutralization for Logs
<1.7.7 : HTTP Response Splitting
<1.6.0 : Log Injection
How to fix: Upgrade github.com/gin-gonic/gin to version 1.9.1 or higher.
Reference: https://security.snyk.io/package/golang/github.com%2Fgin-gonic%2Fgin
1. Vulnerability: Origin Header Injection
2. Description: The application is vulnerable to an Origin Header Injection attack due to insufficient validation of the Origin header in CORS (Cross-Origin Resource Sharing) policy implementation.
3. Payload: Origin: http://test.nc.storage.evil.com
4. Step to reproduce:
a. Include the Origin header with a crafted value containing a subdomain ending with ".nc.storage" and a malicious domain, such as "test.nc.storage.evil.com".
b. Send POST request to '/storage/send'
c. The injected value "test.nc.storage.evil.com" matches the regex pattern and is considered a valid origin.
d. As a result, the server sets the Access-Control-Allow-Origin header to "test.nc.storage.evil.com" and allows the request to proceed. If the server responds with an HTTP status code indicating successful access (e.g., 200 OK), the vulnerability is confirmed.
e. This enables the attacker to bypass Cross-Origin Resource Sharing (CORS) restrictions and potentially execute unauthorized actions on behalf of the victim.
5. Impact: The attacker can access sensitive endpoints or data intended only for trusted origins, leading to information disclosure or data manipulation.
6. Mitigation: Update the regular expression used for validating the Origin header to enforce stricter validation criteria.
7. Reference: https://portswigger.net/web-security/cors#how-to-prevent-cors-based-attacks
const express = require('express');
// <4.19.2 & >=5.0.0-alpha.1 < 5.0.0-beta.3 -> Vulnerable to Open Redirect
// <3.11.0 & >=4.0.0 < 4.5.0 -> Vulnerable to XSS
// How to fix: Upgrade express to version 4.19.2, 5.0.0-beta.3 or higher
// Reference: https://security.snyk.io/package/npm/express
const { graphqlHTTP } = require('express-graphql');
// >=0.4.0 <0.4.11 : Cross-site Scripting (XSS)
// How to fix: Upgrade express-graphql to version 0.4.11 or higher.
// Reference: https://security.snyk.io/package/npm/express-graphql
const {
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
GraphQLList,
GraphQLNonNull,
} = require('graphql');
// >=16.3.0 <16.8.1 : DOS
// How to fix: Upgrade graphql to version 16.8.1 or higher.
// Reference: https://security.snyk.io/package/npm/graphql
const mysql = require('mysql2');
const app = express();
const PORT = 5000;
const connection = mysql.createConnection({
host: 'localhost',
user: 'username',
password: 'password',
database: 'database',
multipleStatements: true,
ssl : {
rejectUnauthorized: false
// rejectUnauthorized: false -> disables SSL certificate verification
// Application will accept any SSL certificate presented by the server, regardless of its validity. This poses a significant security risk as it opens your application to potential man-in-the-middle attacks, where an attacker could intercept and tamper with the communication between your application and the server.
// Mitigation: In a production, it's highly recommended to set rejectUnauthorized as true
}
});
connection.connect((err) => { ... });
const BrandType = new GraphQLObjectType({
name: 'Brand',
fields: () => ({
id: { type: GraphQLNonNull(GraphQLString) },
name: { type: GraphQLNonNull(GraphQLString) },
cars: {
type: new GraphQLList(CarType),
resolve: (brand) => {
const query = `SELECT * FROM cars WHERE brandId = '${brand.id}'`;
return new Promise((resolve, reject) => {
connection.query(query, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
});
});
},
},
}),
});
// Vulnerability: SQL injection
// Payload: const query = `SELECT * FROM cars WHERE brandId = '${brand.id}' OR '1'='1';`;
const CarType = new GraphQLObjectType({
name: 'Car',
fields: () => ({
id: { type: GraphQLNonNull(GraphQLString) },
model: { type: GraphQLNonNull(GraphQLString) },
brandId: { type: GraphQLNonNull(GraphQLString) },
brand: {
type: BrandType,
resolve: (car) => {
const query = `SELECT * FROM brands WHERE id = '${car.brandId}'`;
return new Promise((resolve, reject) => {
connection.query(query, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results[0]);
}
});
});
},
},
}),
});
// Vulnerability: SQL injection
// Payload: const query = `SELECT * FROM brands WHERE id = '${car.brandId}' OR '1'='1';`;
const RootQueryType = new GraphQLObjectType({
name: 'Query',
fields: () => ({
car: {
type: CarType,
args: {
id: { type: GraphQLString },
},
resolve: async (parent, args) => {
const query = `SELECT * FROM cars WHERE id = '${args.id}'`;
return new Promise((resolve, reject) => {
connection.query(query, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results[0]);
}
});
});
},
},
cars: {
type: new GraphQLList(CarType),
resolve: async () => {
const query = 'SELECT * FROM cars';
return new Promise((resolve, reject) => {
connection.query(query, (err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
});
});
},
},
}),
});
// Vulnerability: SQL injection
// Payload: const query = `SELECT * FROM cars WHERE id = '${args.id}' OR '1'='1';`;
// Payload: const query = 'SELECT * FROM cars OR '1'='1';';
// Vulnerability: SQL injection
// Description: User inputs are directly interpolated into SQL queries without proper sanitization or parameterization, allowing attackers to inject malicious SQL code.
// Severity: Critical
// Impact: 1. Attackers can retrieve sensitive information from the database 2. Attackers can modify or delete data in the database
// Mitigation: 1. Use Parameterized Queries or Prepared Statements 2. Input Validation and Sanitization
// 3. Use Stored Procedures: Stored Procedures are sets of queries or actions directly stored as objects in the database.
// Reference: https://escape.tech/blog/sql-injection-in-graphql/
const schema = new GraphQLSchema({
query: RootQueryType,
});
app.use('/graphql', graphqlHTTP({ schema, graphiql: true }));
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Package Health:
1. >=0.4.0 <0.4.11 : Cross-site Scripting (XSS)
2. How to fix: Upgrade express-graphql to version 0.4.11 or higher.
3. Reference: https://security.snyk.io/package/npm/express-graphql
Package Health:
1. >=16.3.0 <16.8.1 : DOS
2. How to fix: Upgrade graphql to version 16.8.1 or higher.
3. Reference: https://security.snyk.io/package/npm/graphql
1. rejectUnauthorized: false -> disables SSL certificate verification
2. Application will accept any SSL certificate presented by the server, regardless of its validity. This poses a significant security risk as it opens your application to potential man-in-the-middle attacks, where an attacker could intercept and tamper with the communication between your application and the server.
3. Mitigation: In a production, it's highly recommended to set rejectUnauthorized as true
1. Vulnerability: SQL injection
2. Description: User inputs are directly interpolated into SQL queries without proper sanitization or parameterization, allowing attackers to inject malicious SQL code.
3. Severity: Critical
4. Impact: a. Attackers can retrieve sensitive information from the database b. Attackers can modify or delete data in the database
5. Mitigation: a. Use Parameterized Queries or Prepared Statements b. Input Validation and Sanitization
c. Use Stored Procedures: Stored Procedures are sets of queries or actions directly stored as objects in the database.
6. Reference: https://escape.tech/blog/sql-injection-in-graphql/
import UIKit
import Foundation
class ViewController: UIViewController {
let inputField: UITextField = {
let field = UITextField()
field.translatesAutoresizingMaskIntoConstraints = false
field.placeholder = "URL"
return field
}()
let saveButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Save", for: .normal)
button.addTarget(self, action: #selector(saveInput), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
}
private func setupViews() {
view.addSubview(inputField)
view.addSubview(saveButton)
NSLayoutConstraint.activate([
inputField.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
inputField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
inputField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
saveButton.topAnchor.constraint(equalTo: inputField.bottomAnchor, constant: 40),
saveButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
saveButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
])
}
func getResponseBody(from url: URL, completion: @escaping (Result<Any, Error>) -> Void) {
// Assume this function is returning a json response from the URL provided
return jsonValue
}
func processJSON(json: Any) -> (String, String) {
// Assume this function is returning two strings like:
return (data, fileName)
}
@objc private func saveInput() {
let input = inputField.text!
let url = URL(string: input)!
getResponseBody(from: url) { result in
switch result {
case .success(let json):
let (data, fileName) = processJSON(json: json)
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileUrl = documentsUrl.appendingPathComponent(fileName)
do {
try data.write(to: fileUrl, atomically: true, encoding: .utf8)
print("Success for:\nFile: " + fileName + "\nContent: " + data)
} catch {
print("Error writing file: \(error)")
}
case .failure(let error):
print(error)
}
}
}
// Potential Issues:
// 1. JSON response should properly sanitized before writing it to files to prevent injection attacks when displaying it in the UI(Possibility if website is using same data)
// 2. Optional variables are forcefully unwrapped that can lead to crashing the entire application. Use 'if let' or 'guard let' to unwrap optional variables safely
}
Potential Issues:
1. JSON response should properly sanitized before writing it to files to prevent injection attacks when displaying it in the UI(Possibility if website is using same data)
2. Optional variables are forcefully unwrapped that can lead to crashing the entire application. Use 'if let' or 'guard let' to unwrap optional variables safely
const express = require('express');
// <4.19.2 & >=5.0.0-alpha.1 < 5.0.0-beta.3 -> Vulnerable to Open Redirect
// <3.11.0 & >=4.0.0 < 4.5.0 -> Vulnerable to XSS
// How to fix: Upgrade express to version 4.19.2, 5.0.0-beta.3 or higher
// Reference: https://security.snyk.io/package/npm/express
const redis = require('redis');
// >=2.6.0 <3.1.1 : Regular Expression Denial of Service (ReDoS)
// How to fix: Upgrade redis to version 3.1.1 or higher.
// Reference: https://security.snyk.io/package/npm/redis
const crypto = require('crypto');
const bodyParser = require('body-parser')
const app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
const createRandomSha256 = () => {
return new Promise((resolve, reject) => {
crypto.randomBytes(32, (err, buffer) => {
if (err) {
reject(err);
} else {
const hash = crypto.createHash('sha256');
hash.update(buffer);
const hashHex = hash.digest('hex');
resolve(hashHex);
}
});
});
};
app.post('/password/reset', (req, res) => {
createRandomSha256()
.then((hash) => {
const token = hash;
const email = req.body.email;
const client = redis.createClient();
client.connect()
.then(() => {
client.set(token, email, "EX", 60 * 60);
client.set(email, token, "EX", 60 * 60)
.then(() => {
sendPasswordResetEmail(email, token);
res.send('Password reset email sent');
})
.catch((err) => {
res.status(500).send(err.message);
});
});
})
.catch((err) => {
res.status(500).send(err.message);
});
});
app.post('/password/verify', (req, res) => {
const token = req.body.token;
const email = req.body.email;
const newPassword = req.body.newPassword;
const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
if (newPassword){
if (emailRegex.test(email)) {
const client = redis.createClient();
let keepGoing = true;
client.connect()
.then(() => {
client.keys(token)
.then((keys) => {
if (keys && keys.length > 0) {
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
client.get(key)
.then((value) => {
if (value===email) {
if (keepGoing) {
keepGoing = false;
setNewPassword(email, newPassword);
res.status(200).send({result: 'Succcess'});
}
}
})
.catch((err) => {
console.log(err);
res.status(500).send(err.message);
});
}
} else {
res.status(200).send({result: 'Token is not valid'});
}
});
});
} else {
res.status(500).send("not a valid email address");
}
}
});
// Vulnerability: Email Enumeration in '/password/verify'
// Description: An attacker could exploit timing differences in string comparison operations to iteratively guess valid email addresses associated with password reset tokens.
// Steps to reproduce:
// 1. Initiate a password reset request for a known email address to obtain a password reset token.
// 2. Sends multiple password reset requests, each with a different email address.
// 3. By measuring the response times of the server, observe timing differences in the string comparison operation(===).
// Impact: Successful exploitation of this vulnerability allows attackers to enumerate valid email addresses registered in the system.
// Mitigation: 1. Use Constant-Time Comparison 2. Implement Rate Limiting
// Vulnerability: Token Not Revoking After Password Reset
// Description: Failure to revoke token, allowing attackers to reuse the same token multiple times within the token's expiration period.
// Steps to reproduce:
// 1. Initiate a password reset request for a user account to obtain a password reset token.
// 2. Use the obtained token to reset the password for the targeted user account as intended.
// 3. Instead of discarding the token after resetting the password, retain the token.
// 4. This token can be used multiple times within 60 * 60 time period
// Impact: Successful exploitation of this vulnerability allows attackers to repeatedly reset passwords for user accounts within the token's expiration period.
// Mitigation: Modify the password reset functionality to revoke tokens immediately after they have been used once.
function sendPasswordResetEmail(email, token){
// Assume this function works as expected
return true
}
function setNewPassword(email, password){
// Assume this function works as expected
return true
}
app.listen(1337, () => {
console.log("Server listening on port 1337");
});
Package Health:
1. <4.19.2 & >=5.0.0-alpha.1 < 5.0.0-beta.3 -> Vulnerable to Open Redirect
2. <3.11.0 & >=4.0.0 < 4.5.0 -> Vulnerable to XSS
3. How to fix: Upgrade express to version 4.19.2, 5.0.0-beta.3 or higher
4. Reference: https://security.snyk.io/package/npm/express
Package Health:
1. >=2.6.0 <3.1.1 : Regular Expression Denial of Service (ReDoS)
2. How to fix: Upgrade redis to version 3.1.1 or higher.
3. Reference: https://security.snyk.io/package/npm/redis
1. Vulnerability: Email Enumeration in '/password/verify'
2. Description: An attacker could exploit timing differences in string comparison operations to iteratively guess valid email addresses associated with password reset tokens.
3. Steps to reproduce:
a. Initiate a password reset request for a known email address to obtain a password reset token.
b. Sends multiple password reset requests, each with a different email address.
c. By measuring the response times of the server, observe timing differences in the string comparison operation(===).
4. Impact: Successful exploitation of this vulnerability allows attackers to enumerate valid email addresses registered in the system.
5. Mitigation: 1. Use Constant-Time Comparison 2. Implement Rate Limiting
1. Vulnerability: Token Not Revoking After Password Reset
2.. Description: Failure to revoke token, allowing attackers to reuse the same token multiple times within the token's expiration period.
3. Steps to reproduce:
a. Initiate a password reset request for a user account to obtain a password reset token.
b. Use the obtained token to reset the password for the targeted user account as intended.
c. Instead of discarding the token after resetting the password, retain the token.
d. This token can be used multiple times within 60 * 60 time period
4. Impact: Successful exploitation of this vulnerability allows attackers to repeatedly reset passwords for user accounts within the token's expiration period.
5. Mitigation: Modify the password reset functionality to revoke tokens immediately after they have been used once.
// MainActivity.java
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
EditText pincodeText;
Button confirmButton;
public static final String APP_PREF = "mySharedPreferences";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
confirmButton = (Button) findViewById(R.id.confirmButton)
confirmButton.setOnClickListener(new View.OnClickListener() { @Override
public void onClick(View v) {
checkPin();
}
})
}
protected void checkPin() {
pincodeText = (EditText) findViewById(R.id.inputPin);
Final String validPin = readValidPin);
String insertedPin = pincodeText.getText().toString();
if (validPin.equals(insertedPin)) {
Intent intent_protected_activity = new Intent(this, protected activity.class); startActivity(intent_protected_activity); }
else {
Toast.makeText(MainActivity.this, "PIN is not correct, please try again.", Toast.LENGTH SHORT).show();
pincodeText.getText().clear();
}
protected String readValidPin() {
// Assume the pin is long enough to avoid brute forcing SharedPreferences settings getSharedPreferences(APP_PREF, 0);
String validPin = settings.getString("pin", null);
return validPin;
} }
// manifest.xml
// Vulnerability: Insecure Storage of Sensitive Information
// Description: Insecure data storage vulnerabilities occur when application store sensitive information such as usernames, passwords, and credit card numbers in plain text.
// Steps to Reproduce:
// 1. A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them. Each SharedPreferences file is managed by the framework and can be private or shared.
// 2. Android stores Shared Preferences settings as an XML file in the shared_prefs folder under DATA/data/{application package} directory. The DATA folder can be obtained by calling Environment. getDataDirectory()
// 3. If sensitive data stored in plaintext in SharedPreferences, it will be easily accessible to anyone with access to the device or the application's data files.
// Impact: An attacker who gains access to the device or the application's data files can easily retrieve the plaintext PIN code from the SharedPreferences.
// Mitigation: Implement encryption mechanisms, such as Android Keystore or encryption libraries, to securely store sensitive information like PIN codes.
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package-"nc.bank" android:versionCode="1"
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" /> <application
android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppThe <activity android:name="MainActivity" android:exported-"true" android:label="@string/title_activity_main">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<intent-filter>
<<Jactivity>
<activity android:name="nc.bank.protected activity" android: exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION VIEN <category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="ncbank" android:pathPrefix="/" android:hostยป "personal details"
<intent-filter>
</activity>
</application>
</manifest>
1. Vulnerability: Cleartext Storage of Sensitive Information (CWE-312)
2. Description: Cleartext data storage vulnerabilities occur when application store sensitive information such as usernames, passwords, and credit card numbers in plain text.
3. Steps to Reproduce:
a. A SharedPreferences object points to a file containing key-value pairs and provides simple methods to read and write them. Each SharedPreferences file is managed by the framework and can be private or shared.
b. Android stores Shared Preferences settings as an XML file in the shared_prefs folder under DATA/data/{application package} directory. The DATA folder can be obtained by calling Environment. getDataDirectory()
c. If sensitive data stored in plaintext in SharedPreferences, it will be easily accessible to anyone with access to the device or the application's data files.
4. Impact: An attacker who gains access to the device or the application's data files can easily retrieve the plaintext PIN code from the SharedPreferences.
5. Mitigation: Implement encryption mechanisms, such as Android Keystore or encryption libraries, to securely store sensitive information like PIN codes.
6. Reference: https://cwe.mitre.org/data/definitions/312.html