-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit f9a5586
Showing
44 changed files
with
2,539 additions
and
0 deletions.
There are no files selected for viewing
Binary file not shown.
Large diffs are not rendered by default.
Oops, something went wrong.
7 changes: 7 additions & 0 deletions
7
Security101.xcodeproj/project.xcworkspace/contents.xcworkspacedata
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
8 changes: 8 additions & 0 deletions
8
Security101.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 added
BIN
+159 KB
...eproj/project.xcworkspace/xcuserdata/ekscrypto.xcuserdatad/UserInterfaceState.xcuserstate
Binary file not shown.
14 changes: 14 additions & 0 deletions
14
Security101.xcodeproj/xcuserdata/ekscrypto.xcuserdatad/xcschemes/xcschememanagement.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
11
Security101/Assets.xcassets/AccentColor.colorset/Contents.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
58
Security101/Assets.xcassets/AppIcon.appiconset/Contents.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"info" : { | ||
"author" : "xcode", | ||
"version" : 1 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.