Skip to content

Commit

Permalink
Final report and testcase update
Browse files Browse the repository at this point in the history
  • Loading branch information
Rana-KV committed Mar 22, 2023
1 parent 4717afa commit 6347974
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 21 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ db.sqlite3
**/__pycache__
LegacySite/migrations
Keys/Master_key
.vscode/
.binary
5 changes: 2 additions & 3 deletions LegacySite/extras.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,12 @@ def parse_card_data(card_file_data, card_path_name):
key = get_key()
key = base64.urlsafe_b64encode(key)
fernet = Fernet(key)
decrypted_data = fernet.decrypt(card_file_data)
decrypted_data = fernet.decrypt(card_file_data).decode()
return decrypted_data
except InvalidToken:
try:
print(get_old_keys())
F = MultiFernet(get_old_keys())
decrypted_data = F.decrypt(card_file_data)
decrypted_data = F.decrypt(card_file_data).decode()
return decrypted_data
except InvalidToken:
pass
Expand Down
2 changes: 1 addition & 1 deletion LegacySite/fixtures/testdata.json

Large diffs are not rendered by default.

33 changes: 20 additions & 13 deletions LegacySite/tests.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.test import TestCase, Client
from LegacySite.models import Card
import io

# Create your tests here.

Expand All @@ -24,20 +25,10 @@ def test_xss_alert(self):
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'alert("hello")')

def test_xss_alert2(self):
self.client = Client(enforce_csrf_checks=True)
# Login has to be done before accessing gift page
self.client.login(username='testuser', password='test')

# Make a GET request with XSS payload
response = self.client.get('http://localhost:8000/gift/1?director=<script>alert("hello")</script>')
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, 'alert("hello")')

def test_xsrf_POST(self):
self.client = Client(enforce_csrf_checks=True)
# Login has to be done before accessing gift page
self.client.login(username='testuser', password='test')
self.client.login(username='test', password='test')

# Make a POST request without a CSRF token
response = self.client.post('http://localhost:8000/gift/1', {'username': 'test2', 'amount': ''})
Expand All @@ -46,7 +37,7 @@ def test_xsrf_POST(self):
def test_SQLi_POST(self):
self.client = Client()
# Login has to be done before accessing gift page
self.client.login(username='testuser', password='test')
self.client.login(username='test', password='test')

#POST Exploitable giftcard to web application
with open('part-1/sqli.gftcrd', 'rb') as f:
Expand All @@ -70,11 +61,27 @@ def test_SQLi_POST(self):
def test_Cmdi_POST(self):
self.client = Client()
# Login has to be done before accessing gift page
self.client.login(username='testuser', password='test')
self.client.login(username='test', password='test')
with open('part-1/cmdi_exp.txt', 'rb') as f:
response = self.client.post('http://localhost:8000/use', {'card_data': f,'card_supplied':'True', 'card_fname':'text | touch test.txt;'})
try:
with open('test.txt', 'rb') as f:
raise "Error"
except:
pass

def test_buy_and_use(self):
client = Client()
client.login(username='test', password='test')
response = client.post('/buy/2', {'amount': 100})
self.assertEqual(response.status_code, 200)
card_data = response.content
response = client.post('/use.html',
{
'card_supplied': 'True',
'card_fname': 'Test',
'card_data': io.BytesIO(card_data),
}
)
self.assertEqual(response.status_code, 200)
self.assertIn(b'Card used!', response.content)
2 changes: 1 addition & 1 deletion LegacySite/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def use_card_view(request):
# KG: data seems dangerous.
# PKV: Fixed SQLi
try:
signature = extras.hash_file(card_data)
signature = extras.hash_file(card_data.encode())
except:
return HttpResponse("Error 400: Bad Request")
# signatures should be pretty unique, right?
Expand Down
2 changes: 1 addition & 1 deletion binary
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"merchant_id": " ",
"customer_id": " �lN�U",
"customer_id": " ��i^7V",
"total_value": 0,
"records": [
]
Expand Down
2 changes: 1 addition & 1 deletion part-1/sqli.gftcrd
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"merchant_id": "NYU Apparel Card", "customer_id": "testuser", "total_value": 95, "records": [{"record_type": "amount_change", "amount_added": 2000, "signature": "377a338452f43e2b9d4cb9145dbc6c77%' union select password from LegacySite_user where LegacySite_user.username='admin'--"}]}
{"merchant_id": "NYU Apparel Card", "customer_id": "test", "total_value": 95, "records": [{"record_type": "amount_change", "amount_added": 2000, "signature": "377a338452f43e2b9d4cb9145dbc6c77%' union select password from LegacySite_user where LegacySite_user.username='admin'--"}]}
2 changes: 1 addition & 1 deletion part-1/sqli2.gftcrd
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"merchant_id": "NYU Apparel Card", "customer_id": "testuser", "total_value": 95, "records": [{"record_type": "amount_change", "amount_added": 2000, "signature": "377a338452f43e2b9d4cb9145dbc6c77 union select password from LegacySite_user where LegacySite_user.username='admin'--"}]}
{"merchant_id": "NYU Apparel Card", "customer_id": "test", "total_value": 95, "records": [{"record_type": "amount_change", "amount_added": 2000, "signature": "377a338452f43e2b9d4cb9145dbc6c77 union select password from LegacySite_user where LegacySite_user.username='admin'--"}]}
67 changes: 67 additions & 0 deletions part-2/encryption_explanation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
Overview of Part-2:

Card.data in DB -
The data type is Charfield with max size of 100, where the hash (SHA-256) of the plain card data is stored. The Plain text format of Card.data has signature (16 Bytes random value) and time stamp to make sure no two giftcards produce the same Hash.
This is used because a card.data is never updated or use based on independent fields. The encryption and decryption is not performed as keeping a Hash value will reduce re-encrypting the DB when keys are rotated.
So having hash of the card.data will ensure that no matter if the data is leaked the attacker can not create corresponding input which produces the required hash value found in the DB.

Giftcard for user -
The giftcard downloaded by a user is plain card.data encrypted using the current encryption key of the application.
This way even if a user receives the giftcard he doesn't know the contents of the giftcard.
Using Djferent the card.data is encrypted using ferent encryption, the card.data has two values - timestamp and random 16 byte signature.
The key used for encryption is derived from SECRET_KEY variable using KEYS_INFO.

Use Giftcard view -
The use giftcard feature is updated accroding to data format decalred in the Card model (charfield).
If a giftcard is uploaded first the giftcard are decrypted using the current derived key.
If the decryption fails then decryption is tried using the revoked keys.
Once the giftcard is successfully decrypted the plain text form of giftcard data is hashed
The Hash value is used to match the card.data in the DB.

Key Management -

This Key Management methodology was inspired from automotive industry KMS solutions deployed for ECUs

Master Key --
The master key used for deriving encryption key is the SECRET_KEY in settings.py.
This value is actually set by reading Master_key file in Keys folder.
In real life scenario this key can be stored only in the production server with only few application having access to it.
The Master_key file is added in .gitignore, so that the production Master key is not leaked.
For the testing purpose a random value is to this file during run time.

Current Key --
The master key used for deriving the current encryption key using the KEYS_INFO value in settings.py
KEYS_INFO defines how many rounds the Master_key has to be proccessed and how many key are revoked.
For KEYS_INFO mentioned 3 rounds and 0 revoked keys. It means the Master_key has to be hashed 3 times to get the current key.
For KEYS_INFO mentioned 3 rounds and 1 revoked key. It means the Master_key has to be hashed 2 times to get the current key.

Revoked Keys and Key Rotation --
Current encryption key can be revoked using the KEYS_INFO value in settings.py
This way the application will stop signing with the revoked key, but it willc ontinue to support giftcard generated by revoked keys.
The backward compatiability is achieved by using MulitFerent function which can decrypt a data suing multiple keys.
If a giftcard fails decryption by the current key then the giftcard is tried to decrypt using multiferent will all the revoked keys.

Using this method only one Master_key is required to be stored ina secured manner probably in a HSM.
In real life situations having and storing too many keys is costly and complex process.
Since a good hash function makes it nearly impossible to compute the inverse of the hash output and collision,
the deriveded keys are peusdorandom for the application but random for attacker.

For the purpose of testing a random value of the Master_key is intiated during run time using the github actions.

Threat Model -
Assumptions --
Proper Authentication and Authorization is performed in production server before any program accesses a Secure files (like Master_key).
Attacker is not able to extract runtime keys from the production application.
Keys are properly rotated at regular intervals of time.
SHA-256 Hash function can prevent attackers from finding the corresponding input for a given output (used for keys and giftcard data in DB).

Issues Faced -
When card.data was hashed and stored in the DB many functinality was broken is the views.py.
The encryption and decryption is happening outside the DB in extra.py this lead to frequent chanegs in code segment to provide the data in expected format.
Key rotation part was a huge hurdle and using multiferent function the key rotation was added easily.

Additonal changes -
The application sends a notification if a card is reused.



0 comments on commit 6347974

Please sign in to comment.