Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
sassiwalid committed Oct 24, 2024
0 parents commit f9a5586
Show file tree
Hide file tree
Showing 44 changed files with 2,539 additions and 0 deletions.
Binary file added .DS_Store
Binary file not shown.
746 changes: 746 additions & 0 deletions Security101.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>Security101.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>
11 changes: 11 additions & 0 deletions Security101/Assets.xcassets/AccentColor.colorset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
58 changes: 58 additions & 0 deletions Security101/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"images" : [
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
6 changes: 6 additions & 0 deletions Security101/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
50 changes: 50 additions & 0 deletions Security101/ContentView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// Security101
//
// Created by Dave Poirier on 2024-09-14.
//

import SwiftUI

struct ContentView: View {

enum Tab {
case level0, level1, level2, level3, level4, level5
}

@State var tab: Tab = .level0

var body: some View {
VStack(spacing: 0) {
HStack {
TabView(label: "Level 0", tab: $tab, tabId: .level0)
TabView(label: "Level 1", tab: $tab, tabId: .level1)
TabView(label: "Level 2", tab: $tab, tabId: .level2)
TabView(label: "Level 3", tab: $tab, tabId: .level3)
TabView(label: "Level 4", tab: $tab, tabId: .level4)
TabView(label: "Level 5", tab: $tab, tabId: .level5)
}
.frame(height: 30)
Divider()

switch tab {
case .level0:
Level0GlobalView()
case .level1:
Level1GlobalView()
case .level2:
Level2GlobalView()
case .level3:
Level3GlobalView()
case .level4:
Level4GlobalView()
case .level5:
Level5GlobalView()
}
}
}
}

#Preview {
ContentView()
}
14 changes: 14 additions & 0 deletions Security101/Hash.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Security101
//
// Created by Dave Poirier on 2024-09-14.
//

import Foundation
import CryptoKit

func hash(_ value: String) -> String {
let data = value.data(using: .utf8)!
let hashed = Data(SHA512.hash(data: data))
return hashed.base64EncodedString()
}
37 changes: 37 additions & 0 deletions Security101/Level 0/Level0ClientView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// Security101
//
// Created by Dave Poirier on 2024-09-14.
//

import SwiftUI

struct Level0ClientView: View {
let server: Level0Server
@State var username: String = ""
@State var password: String = ""
@State var signedIn: Bool = false

var body: some View {
VStack {
if signedIn {
Text("Signed in!").bold()
Text("Username used: \(username)")
Text("Password used: \(password)")
Button("Sign out") { signedIn = false }
} else {
TextField("Username", text: $username)
TextField("Password", text: $password)
Button("Submit") { try? signIn() }
}
}
.padding()
}

private func signIn() throws {
let auth = Level0Server.AuthRequest(username: username, password: password)
let request = try JSONEncoder().encode(auth)
signedIn = server.authorize(request)
}
}

56 changes: 56 additions & 0 deletions Security101/Level 0/Level0GlobalView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// Security101
//
// Created by Dave Poirier on 2024-09-14.
//

import SwiftUI

struct Level0GlobalView: View {
let server = Level0Server()

var body: some View {
VStack {
HStack(alignment: .top) {
ScrollView {
Text("Server stores the password in plaintext. A data breach would expose all the users passwords allowing the attacker to sign in with any account, at any time unless the users reset their password.\n\nRequests are sent in plaintext, so if they are intercepted, the attacker also gains knowledge of the password AND can replay the sign-in request as-is to gain access to that specific user account.\n\nUsername + Password -> Request -> Token")
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
}
.background(Color(hue: 0, saturation: 1.0, brightness: 0.0, opacity: 0.1))
.frame(width: 300)

VStack {
Text("Client").font(.title)
Level0ClientView(server: server)
.frame(height: 400, alignment: .center)
}
.padding()
.frame(width: 300)

VStack {
Text("Man-in-the-middle").font(.title)
Level0SpyView(server: server)
.frame(maxHeight: .infinity)
}
.padding()
.overlay(Rectangle()
.stroke(style: StrokeStyle(lineWidth: 3, lineCap: .round, dash: [20, 8]))
.foregroundColor(.red)
)
.frame(width: 300)

VStack {
Text("Server").font(.title)
ServerView(server: server)
.frame(maxHeight: .infinity)
}
.padding()
.frame(width: 300)
}

Text("Plaintext passwords in server database, plaintext requests")
.padding()
}
}
}
41 changes: 41 additions & 0 deletions Security101/Level 0/Level0Server.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Security101
//
// Created by Dave Poirier on 2024-09-14.
//

import Foundation
import Combine

class Level0Server: ServerInstance {

struct AuthRequest: Sendable, Codable {
let username: String
let password: String
}

private var username: String = "ekscrypto"
private var password: String = "LetMeIn"

var authInfo: AnyPublisher<AuthInfo, Never> {
Just(AuthInfo(username: username, password: password)).eraseToAnyPublisher()
}

let log: CurrentValueSubject<AuthRequestLog, Never> = .init(.noRequestYet)
var lastAuthRequest: AnyPublisher<AuthRequestLog, Never> { log.eraseToAnyPublisher() }

let spy: CurrentValueSubject<Data, Never> = .init(Data())
var spyHook: AnyPublisher<Data, Never> { spy.eraseToAnyPublisher() }

func authorize(_ request: Data) -> Bool {
spy.send(request)

guard let auth = try? JSONDecoder().decode(AuthRequest.self, from: request) else {
return false
}

let granted = auth.username == username && auth.password == password
log.send(AuthRequestLog(request: "\(auth)", accessGranted: granted))
return granted
}
}
67 changes: 67 additions & 0 deletions Security101/Level 0/Level0SpyView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// Security101
//
// Created by Dave Poirier on 2024-09-14.
//

import SwiftUI

struct Level0SpyView: View {
let server: ServerInstance
@State var request: String = ""
@State var capturedRequest: Data = Data()
@State var signedIn = false
@State var username: String = ""
@State var password: String = ""

var body: some View {
VStack(alignment: .leading) {
Text("Captured Request").bold()
Text(request)
.frame(maxWidth: .infinity)


Divider()
if signedIn {
Text("Signed in!").bold()
Button("Sign out") {
signedIn = false
request = ""
capturedRequest = Data()
}
} else {
Text("Unauthorized")

GroupBox("Replay Attack") {
Button("Attempt replay attack") { replayAttack() }.disabled(request.isEmpty)
}

GroupBox("Authenticate") {
TextField("Username", text: $username)
TextField("Password", text: $password)
Button("Sign in") { try? signIn() }
}
}
}
.onReceive(server.spyHook) { newRequest in
capturedRequest = newRequest
request = String(data: newRequest, encoding: .utf8) ?? ""
}
}

func replayAttack() {
signedIn = server.authorize(capturedRequest)
}

private func signIn() throws {
let auth = Level0Server.AuthRequest(username: username, password: password)
let request = try JSONEncoder().encode(auth)
signedIn = server.authorize(request)
}

}

#Preview {
Level0SpyView(server: Level0Server())
.frame(width: 300)
}
Loading

0 comments on commit f9a5586

Please sign in to comment.