diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..45cf751 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,11 @@ +# Changelog + +## [0.0.2] - 2024-01-05 + +### Added +- Unknown signed string tab. +- Enabled signers setting added to the main tab +- _Known keys_ brute force technic added to the Attack mode + +### Changed +- Upgrade dependencies: org.json:json \ No newline at end of file diff --git a/README.md b/README.md index 5fda024..f3cadf9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Sessionless -Sessionless is a Burp Suite extension for editing, signing, verifying, attacking signed tokens: [Django TimestampSigner](https://docs.djangoproject.com/en/5.0/topics/signing/#verifying-timestamped-values), [ItsDangerous Signer](https://itsdangerous.palletsprojects.com/en/2.1.x/signer/), [Express cookie-session middleware](https://expressjs.com/en/resources/middleware/cookie-session.html), [OAuth2 Proxy](https://github.com/oauth2-proxy/oauth2-proxy) and [Tornado’s signed cookies](https://www.tornadoweb.org/en/stable/guide/security.html). +Sessionless is a Burp Suite extension for editing, signing, verifying, attacking signed tokens: [Django TimestampSigner](https://docs.djangoproject.com/en/5.0/topics/signing/#verifying-timestamped-values), [ItsDangerous Signer](https://itsdangerous.palletsprojects.com/en/2.1.x/signer/), [Express cookie-session middleware](https://expressjs.com/en/resources/middleware/cookie-session.html), [OAuth2 Proxy](https://github.com/oauth2-proxy/oauth2-proxy), [Tornado’s signed cookies](https://www.tornadoweb.org/en/stable/guide/security.html) and Unknown signed string. It provides automatic detection and in-line editing of token within HTTP requests/responses and WebSocket messages, signing of tokens and automation of brute force attacks against signed tokens implementations. @@ -25,6 +25,14 @@ The `Editor View` supports a number of signed tokens: Django, Dangerous, Flask, The Dangerous tab can be used for both, `Flask` and `Django` tokens, which are selected depending on whether a Dangerous or Django token is detected. +The Unknown tab can be used to brute force unknown signed strings. Guessing mode works only with _Balanced_ brute force attack. It supports different message derivation technics, including: + +* _None_ message will be used as is +* _CONCAT_ separator byte will be removed from the message and that new value will be used to calculate signature +* _Tornado_ separator byte will be added to the end of the message string + + + ### Editable Fields A JSON text editor is provided to edit each component that contain JSON content: @@ -55,6 +63,7 @@ A hex editor is provided to all signed tokens, except Express signatures. __NOTE The `Brute force` option implements three types of attacks against signed tokens Signatures: +* _Known keys_ will use previously found secret keys only * _Fast_ will use default hashing algorithm and key derivation * _Balanced_ will use all known key derivation technics, except PBKDF2HMAC * _Deep_ will use all key derivation technics, including PBKDF2HMAC diff --git a/build.gradle b/build.gradle index 44ec7c4..ae24793 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group = 'one.d4d' -version = '0.0.1' +version = '0.0.2' description = 'token-signer' repositories { @@ -37,7 +37,7 @@ dependencies { 'com.nimbusds:nimbus-jose-jwt:9.21', 'org.exbin.deltahex:deltahex-swing:0.1.2', 'com.fifesoft:rsyntaxtextarea:3.3.3', - 'org.json:json:20230227', + 'org.json:json:20231013', 'org.apache.commons:commons-lang3:3.12.0' ) testImplementation( diff --git a/gitimg/unknown_tab.png b/gitimg/unknown_tab.png new file mode 100644 index 0000000..bfcf0e1 Binary files /dev/null and b/gitimg/unknown_tab.png differ diff --git a/src/main/java/burp/SessionlessExtension.java b/src/main/java/burp/SessionlessExtension.java index 9a11727..08587e4 100644 --- a/src/main/java/burp/SessionlessExtension.java +++ b/src/main/java/burp/SessionlessExtension.java @@ -6,11 +6,7 @@ import burp.api.montoya.proxy.Proxy; import burp.api.montoya.ui.UserInterface; import burp.api.montoya.utilities.ByteUtils; -import burp.config.BurpConfig; -import burp.config.BurpConfigPersistence; -import burp.config.BurpKeysModelPersistence; -import burp.config.KeysModel; -import burp.proxy.ProxyConfig; +import burp.config.*; import burp.proxy.ProxyHttpMessageHandler; import burp.proxy.ProxyWsMessageHandler; import one.d4d.sessionless.forms.ExtensionTab; @@ -43,6 +39,11 @@ public void initialize(MontoyaApi api) { UserInterface userInterface = api.userInterface(); Window suiteWindow = userInterface.swingUtils().suiteFrame(); + Proxy proxy = api.proxy(); + ProxyConfig proxyConfig = burpConfig.proxyConfig(); + SignerConfig signerConfig = burpConfig.signerConfig(); + ByteUtils byteUtils = api.utilities().byteUtils(); + boolean isProVersion = api.burpSuite().version().edition() == PROFESSIONAL; RstaFactory rstaFactory = new RstaFactory(userInterface, api.logging()); @@ -64,6 +65,7 @@ public void initialize(MontoyaApi api) { api.logging(), api.userInterface(), api.collaborator().defaultPayloadGenerator(), + signerConfig, editorCreationContext.editorMode() != READ_ONLY, isProVersion ) @@ -76,20 +78,17 @@ public void initialize(MontoyaApi api) { api.logging(), api.userInterface(), api.collaborator().defaultPayloadGenerator(), + signerConfig, editorCreationContext.editorMode() != READ_ONLY, isProVersion ) ); - Proxy proxy = api.proxy(); - ProxyConfig proxyConfig = burpConfig.proxyConfig(); - ByteUtils byteUtils = api.utilities().byteUtils(); - - ProxyHttpMessageHandler proxyHttpMessageHandler = new ProxyHttpMessageHandler(proxyConfig, byteUtils); + ProxyHttpMessageHandler proxyHttpMessageHandler = new ProxyHttpMessageHandler(proxyConfig, signerConfig, byteUtils); proxy.registerRequestHandler(proxyHttpMessageHandler); proxy.registerResponseHandler(proxyHttpMessageHandler); - ProxyWsMessageHandler proxyWsMessageHandler = new ProxyWsMessageHandler(proxyConfig, byteUtils); + ProxyWsMessageHandler proxyWsMessageHandler = new ProxyWsMessageHandler(proxyConfig, signerConfig, byteUtils); proxy.registerWebSocketCreationHandler(proxyWebSocketCreation -> proxyWebSocketCreation.proxyWebSocket().registerProxyMessageHandler(proxyWsMessageHandler) ); diff --git a/src/main/java/burp/config/BurpConfig.java b/src/main/java/burp/config/BurpConfig.java index 6055e59..44ee2e7 100644 --- a/src/main/java/burp/config/BurpConfig.java +++ b/src/main/java/burp/config/BurpConfig.java @@ -1,13 +1,16 @@ package burp.config; -import burp.proxy.ProxyConfig; import com.google.gson.annotations.Expose; public class BurpConfig { private final @Expose ProxyConfig proxyConfig = new ProxyConfig(); + private final @Expose SignerConfig signerConfig = new SignerConfig(); public ProxyConfig proxyConfig() { return proxyConfig; } + public SignerConfig signerConfig() { + return signerConfig; + } } diff --git a/src/main/java/burp/proxy/ProxyConfig.java b/src/main/java/burp/config/ProxyConfig.java similarity index 95% rename from src/main/java/burp/proxy/ProxyConfig.java rename to src/main/java/burp/config/ProxyConfig.java index 427a5c1..332d037 100644 --- a/src/main/java/burp/proxy/ProxyConfig.java +++ b/src/main/java/burp/config/ProxyConfig.java @@ -1,5 +1,6 @@ -package burp.proxy; +package burp.config; +import burp.proxy.HighlightColor; import com.google.gson.annotations.Expose; import one.d4d.sessionless.utils.Utils; diff --git a/src/main/java/burp/config/SignerConfig.java b/src/main/java/burp/config/SignerConfig.java new file mode 100644 index 0000000..671477e --- /dev/null +++ b/src/main/java/burp/config/SignerConfig.java @@ -0,0 +1,64 @@ +package burp.config; + +import com.google.gson.annotations.Expose; + +public class SignerConfig { + @Expose + private boolean enableDangerous; + @Expose + private boolean enableExpress; + @Expose + private boolean enableOAuth; + @Expose + private boolean enableTornado; + @Expose + private boolean enableUnknown; + + public SignerConfig() { + this.enableDangerous = true; + this.enableExpress = true; + this.enableOAuth = false; + this.enableTornado = true; + this.enableUnknown = false; + } + + public boolean isEnableDangerous() { + return enableDangerous; + } + + public void setEnableDangerous(boolean enableDangerous) { + this.enableDangerous = enableDangerous; + } + + public boolean isEnableExpress() { + return enableExpress; + } + + public void setEnableExpress(boolean enableExpress) { + this.enableExpress = enableExpress; + } + + public boolean isEnableOAuth() { + return enableOAuth; + } + + public void setEnableOAuth(boolean enableOAuth) { + this.enableOAuth = enableOAuth; + } + + public boolean isEnableTornado() { + return enableTornado; + } + + public void setEnableTornado(boolean enableTornado) { + this.enableTornado = enableTornado; + } + + public boolean isEnableUnknown() { + return enableUnknown; + } + + public void setEnableUnknown(boolean enableUnknown) { + this.enableUnknown = enableUnknown; + } +} diff --git a/src/main/java/burp/proxy/AnnotationsModifier.java b/src/main/java/burp/proxy/AnnotationsModifier.java index e207ab5..09994a2 100644 --- a/src/main/java/burp/proxy/AnnotationsModifier.java +++ b/src/main/java/burp/proxy/AnnotationsModifier.java @@ -5,6 +5,8 @@ import burp.api.montoya.http.message.Cookie; import burp.api.montoya.http.message.params.ParsedHttpParameter; import burp.api.montoya.utilities.ByteUtils; +import burp.config.ProxyConfig; +import burp.config.SignerConfig; import one.d4d.sessionless.itsdangerous.model.SignedTokenObjectFinder; import java.util.List; @@ -12,10 +14,12 @@ class AnnotationsModifier { private final ByteUtils byteUtils; private final ProxyConfig proxyConfig; + private final SignerConfig signerConfig; - AnnotationsModifier(ProxyConfig proxyConfig, ByteUtils byteUtils) { + AnnotationsModifier(ProxyConfig proxyConfig, SignerConfig signerConfig, ByteUtils byteUtils) { this.byteUtils = byteUtils; this.proxyConfig = proxyConfig; + this.signerConfig = signerConfig; } void updateAnnotationsIfApplicable(Annotations annotations, ByteArray data, List cookies, List params) { @@ -37,7 +41,7 @@ private void updateAnnotations(Annotations annotations, String messageString, Li } private Counts countExtractedSignedTokenObjects(String messageString, List cookies, List params) { - int count = SignedTokenObjectFinder.extractSignedTokenObjects(messageString, cookies, params).size(); + int count = SignedTokenObjectFinder.extractSignedTokenObjects(signerConfig, messageString, cookies, params).size(); return new Counts(proxyConfig, count); } diff --git a/src/main/java/burp/proxy/ProxyHttpMessageHandler.java b/src/main/java/burp/proxy/ProxyHttpMessageHandler.java index df3d77f..1c369ec 100644 --- a/src/main/java/burp/proxy/ProxyHttpMessageHandler.java +++ b/src/main/java/burp/proxy/ProxyHttpMessageHandler.java @@ -2,12 +2,14 @@ import burp.api.montoya.proxy.http.*; import burp.api.montoya.utilities.ByteUtils; +import burp.config.ProxyConfig; +import burp.config.SignerConfig; public class ProxyHttpMessageHandler implements ProxyRequestHandler, ProxyResponseHandler { private final AnnotationsModifier annotationsModifier; - public ProxyHttpMessageHandler(ProxyConfig proxyConfig, ByteUtils byteUtils) { - this.annotationsModifier = new AnnotationsModifier(proxyConfig, byteUtils); + public ProxyHttpMessageHandler(ProxyConfig proxyConfig, SignerConfig signerConfig, ByteUtils byteUtils) { + this.annotationsModifier = new AnnotationsModifier(proxyConfig, signerConfig, byteUtils); } @Override diff --git a/src/main/java/burp/proxy/ProxyWsMessageHandler.java b/src/main/java/burp/proxy/ProxyWsMessageHandler.java index b0440c4..0eca6c6 100644 --- a/src/main/java/burp/proxy/ProxyWsMessageHandler.java +++ b/src/main/java/burp/proxy/ProxyWsMessageHandler.java @@ -2,12 +2,14 @@ import burp.api.montoya.proxy.websocket.*; import burp.api.montoya.utilities.ByteUtils; +import burp.config.ProxyConfig; +import burp.config.SignerConfig; public class ProxyWsMessageHandler implements ProxyMessageHandler { private final AnnotationsModifier annotationsModifier; - public ProxyWsMessageHandler(ProxyConfig proxyConfig, ByteUtils byteUtils) { - this.annotationsModifier = new AnnotationsModifier(proxyConfig, byteUtils); + public ProxyWsMessageHandler(ProxyConfig proxyConfig, SignerConfig signerConfig, ByteUtils byteUtils) { + this.annotationsModifier = new AnnotationsModifier(proxyConfig, signerConfig, byteUtils); } @Override diff --git a/src/main/java/one/d4d/sessionless/forms/EditorTab.form b/src/main/java/one/d4d/sessionless/forms/EditorTab.form index 60c0fcb..4fce046 100644 --- a/src/main/java/one/d4d/sessionless/forms/EditorTab.form +++ b/src/main/java/one/d4d/sessionless/forms/EditorTab.form @@ -529,6 +529,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/one/d4d/sessionless/forms/EditorTab.java b/src/main/java/one/d4d/sessionless/forms/EditorTab.java index 656ce56..e578ab6 100644 --- a/src/main/java/one/d4d/sessionless/forms/EditorTab.java +++ b/src/main/java/one/d4d/sessionless/forms/EditorTab.java @@ -3,6 +3,7 @@ import burp.api.montoya.collaborator.CollaboratorPayloadGenerator; import burp.api.montoya.ui.Selection; import burp.api.montoya.ui.editor.extension.ExtensionProvidedEditor; +import burp.config.SignerConfig; import one.d4d.sessionless.hexcodearea.HexCodeAreaFactory; import one.d4d.sessionless.presenter.EditorPresenter; import one.d4d.sessionless.presenter.PresenterStore; @@ -32,12 +33,14 @@ public abstract class EditorTab implements ExtensionProvidedEditor { public static final int TAB_EXPRESS = 1; public static final int TAB_OAUTH = 2; public static final int TAB_TORNADO = 3; + public static final int TAB_UNKNOWN = 4; private static final int MAX_JOSE_OBJECT_STRING_LENGTH = 68; final EditorPresenter presenter; private final RstaFactory rstaFactory; private final boolean editable; private final HexCodeAreaFactory hexCodeAreaFactory; private final boolean isProVersion; + private final SignerConfig signerConfig; private int mode; private JPanel mainPanel; private JTabbedPane tabbedPane; @@ -65,10 +68,14 @@ public abstract class EditorTab implements ExtensionProvidedEditor { private JCheckBox checkBoxDjango; private JCheckBox checkBoxCompress; private JButton buttonAttack; + private RSyntaxTextArea textAreaUnknownStringMessage; + private JPanel panelUnknownStringSeparator; + private RSyntaxTextArea textAreaUnknownStringSignature; private CodeArea codeAreaDangerousSignature; private CodeArea codeAreaDangerousSeparator; private CodeArea codeAreaOAuthSignature; private CodeArea codeAreaTornadoSignature; + private CodeArea codeAreaUnknownSeparator; EditorTab( PresenterStore presenters, @@ -76,17 +83,20 @@ public abstract class EditorTab implements ExtensionProvidedEditor { HexCodeAreaFactory hexAreaCodeFactory, CollaboratorPayloadGenerator collaboratorPayloadGenerator, ErrorLoggingActionListenerFactory actionListenerFactory, + SignerConfig signerConfig, boolean editable, boolean isProVersion) { this.rstaFactory = rstaFactory; this.editable = editable; this.hexCodeAreaFactory = hexAreaCodeFactory; this.isProVersion = isProVersion; + this.signerConfig = signerConfig; this.presenter = new EditorPresenter( this, collaboratorPayloadGenerator, actionListenerFactory, - presenters); + presenters, + signerConfig); DocumentListener documentListener = new DocumentListener() { @Override @@ -116,6 +126,8 @@ public void changedUpdate(DocumentEvent e) { textAreaTornadoTimestamp.getDocument().addDocumentListener(documentListener); textAreaTornadoName.getDocument().addDocumentListener(documentListener); textAreaTornadoValue.getDocument().addDocumentListener(documentListener); + textAreaUnknownStringMessage.getDocument().addDocumentListener(documentListener); + textAreaUnknownStringSignature.getDocument().addDocumentListener(documentListener); checkBoxIsJSON.addActionListener(e -> presenter.componentChanged()); checkBoxDjango.addActionListener(e -> presenter.componentChanged()); @@ -125,6 +137,7 @@ public void changedUpdate(DocumentEvent e) { codeAreaDangerousSeparator.addDataChangedListener(presenter::componentChanged); codeAreaOAuthSignature.addDataChangedListener(presenter::componentChanged); codeAreaTornadoSignature.addDataChangedListener(presenter::componentChanged); + codeAreaUnknownSeparator.addDataChangedListener(presenter::componentChanged); comboBoxSignedToken.addActionListener(e -> presenter.onSelectionChanged()); @@ -294,6 +307,27 @@ public boolean getDangerouseIsCompressed() { public void setDangerouseIsCompressed(boolean enabled) { checkBoxCompress.setSelected(enabled); } + public String getUnknownMessage() { + return textAreaUnknownStringMessage.getText(); + } + + public void setUnknownMessage(String text) { + textAreaUnknownStringMessage.setText(text); + } + public String getUnknownSignature() { + return textAreaUnknownStringSignature.getText(); + } + + public void setUnknownSignature(String signature) { + textAreaUnknownStringSignature.setText(signature); + } + public byte[] getUnknownSeparator() { + return Utils.getCodeAreaData(codeAreaUnknownSeparator); + } + + public void setUnknownSeparator(byte[] separator) { + codeAreaUnknownSeparator.setData(new ByteArrayEditableData(separator)); + } public void setSignedTokenObjects(List signedTokenObjectStrings) { comboBoxSignedToken.setModel(new MaxLengthStringComboBoxModel(MAX_JOSE_OBJECT_STRING_LENGTH, signedTokenObjectStrings)); @@ -321,20 +355,26 @@ private void createUIComponents() { codeAreaTornadoSignature = hexCodeAreaFactory.build(); panelTornadoSignature.add(codeAreaTornadoSignature); + panelUnknownStringSeparator = new JPanel(new BorderLayout()); + codeAreaUnknownSeparator = hexCodeAreaFactory.build(); + panelUnknownStringSeparator.add(codeAreaUnknownSeparator); + // Create the Attack popup menu JPopupMenu popupMenuAttack = new JPopupMenu(); + JMenuItem menuItemAttackBruteForceKnownKeys = new JMenuItem(Utils.getResourceString("editor_view_button_attack_known_keys")); JMenuItem menuItemAttackBruteForce = new JMenuItem(Utils.getResourceString("editor_view_button_attack_fast")); JMenuItem menuItemAttackBruteForceBalanced = new JMenuItem(Utils.getResourceString("editor_view_button_attack_balanced")); JMenuItem menuItemAttackBruteForceDeep = new JMenuItem(Utils.getResourceString("editor_view_button_attack_deep")); // Attach the event handlers to the popup menu click events - + menuItemAttackBruteForceKnownKeys.addActionListener(e -> presenter.onAttackKnownKeysClicked()); menuItemAttackBruteForce.addActionListener(e -> presenter.onAttackFastClicked()); menuItemAttackBruteForceBalanced.addActionListener(e -> presenter.onAttackBalancedClicked()); menuItemAttackBruteForceDeep.addActionListener(e -> presenter.onAttackDeepClicked()); // Add the buttons to the popup menu + popupMenuAttack.add(menuItemAttackBruteForceKnownKeys); popupMenuAttack.add(menuItemAttackBruteForce); popupMenuAttack.add(menuItemAttackBruteForceBalanced); popupMenuAttack.add(menuItemAttackBruteForceDeep); @@ -358,6 +398,8 @@ private void createUIComponents() { textAreaTornadoTimestamp = rstaFactory.buildDefaultTextArea(); textAreaTornadoName = rstaFactory.buildDefaultTextArea(); textAreaTornadoValue = rstaFactory.buildDefaultTextArea(); + textAreaUnknownStringMessage = rstaFactory.buildDefaultTextArea(); + textAreaUnknownStringSignature = rstaFactory.buildDefaultTextArea(); } private void onBruteForceAttackClicked() { @@ -437,7 +479,18 @@ public void setTornadoMode() { EditationAllowed editationAllowed = editable ? ALLOWED : READ_ONLY; codeAreaTornadoSignature.setEditationAllowed(editationAllowed); } + public void setUnknownMode() { + mode = TAB_UNKNOWN; + enableTabAtIndex(TAB_UNKNOWN); + buttonBruteForceAttack.setEnabled(editable); + buttonAttack.setEnabled(editable); + + textAreaUnknownStringMessage.setEditable(editable); + textAreaUnknownStringSignature.setEditable(editable); + EditationAllowed editationAllowed = editable ? ALLOWED : READ_ONLY; + codeAreaUnknownSeparator.setEditationAllowed(editationAllowed); + } @Override public String caption() { diff --git a/src/main/java/one/d4d/sessionless/forms/KeysTableColumns.java b/src/main/java/one/d4d/sessionless/forms/KeysTableColumns.java index 90928e5..25448fd 100644 --- a/src/main/java/one/d4d/sessionless/forms/KeysTableColumns.java +++ b/src/main/java/one/d4d/sessionless/forms/KeysTableColumns.java @@ -6,9 +6,10 @@ enum KeysTableColumns { ID("table_id", 30, String.class), - SECRET("table_secret", 40, String.class), + SECRET("table_secret", 30, String.class), ALGORITHM("table_algorithm", 10, String.class), DERIVATION("table_derivation",10, String.class), + MESSAGE_DERIVATION("table_message_derivation", 10, String.class), DIGEST("table_digest",10, String.class); private final String label; diff --git a/src/main/java/one/d4d/sessionless/forms/KeysTableModel.java b/src/main/java/one/d4d/sessionless/forms/KeysTableModel.java index caf50b0..eb6c0b9 100644 --- a/src/main/java/one/d4d/sessionless/forms/KeysTableModel.java +++ b/src/main/java/one/d4d/sessionless/forms/KeysTableModel.java @@ -49,6 +49,7 @@ public Object getValueAt(int rowIndex, int columnIndex) { case SECRET -> key.getSecret(); case ALGORITHM -> key.getDigestMethod(); case DERIVATION -> key.getKeyDerivation(); + case MESSAGE_DERIVATION -> key.getMessageDerivation(); case DIGEST -> key.getMessageDigestAlgorythm(); }; } diff --git a/src/main/java/one/d4d/sessionless/forms/RequestEditorView.java b/src/main/java/one/d4d/sessionless/forms/RequestEditorView.java index 9d7e716..5f7d8a2 100644 --- a/src/main/java/one/d4d/sessionless/forms/RequestEditorView.java +++ b/src/main/java/one/d4d/sessionless/forms/RequestEditorView.java @@ -7,6 +7,7 @@ import burp.api.montoya.logging.Logging; import burp.api.montoya.ui.UserInterface; import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpRequestEditor; +import burp.config.SignerConfig; import one.d4d.sessionless.hexcodearea.HexCodeAreaFactory; import one.d4d.sessionless.presenter.PresenterStore; import one.d4d.sessionless.rsta.RstaFactory; @@ -25,6 +26,7 @@ public RequestEditorView( Logging logging, UserInterface userInterface, CollaboratorPayloadGenerator collaboratorPayloadGenerator, + SignerConfig signerConfig, boolean editable, boolean isProVersion) { super( @@ -33,6 +35,7 @@ public RequestEditorView( new HexCodeAreaFactory(logging, userInterface), collaboratorPayloadGenerator, new ErrorLoggingActionListenerFactory(logging), + signerConfig, editable, isProVersion ); diff --git a/src/main/java/one/d4d/sessionless/forms/ResponseEditorView.java b/src/main/java/one/d4d/sessionless/forms/ResponseEditorView.java index 0db26e8..0ce6cd1 100644 --- a/src/main/java/one/d4d/sessionless/forms/ResponseEditorView.java +++ b/src/main/java/one/d4d/sessionless/forms/ResponseEditorView.java @@ -7,6 +7,7 @@ import burp.api.montoya.logging.Logging; import burp.api.montoya.ui.UserInterface; import burp.api.montoya.ui.editor.extension.ExtensionProvidedHttpResponseEditor; +import burp.config.SignerConfig; import one.d4d.sessionless.hexcodearea.HexCodeAreaFactory; import one.d4d.sessionless.presenter.PresenterStore; import one.d4d.sessionless.rsta.RstaFactory; @@ -24,6 +25,7 @@ public ResponseEditorView( Logging logging, UserInterface userInterface, CollaboratorPayloadGenerator collaboratorPayloadGenerator, + SignerConfig signerConfig, boolean editable, boolean isProVersion) { super( @@ -32,6 +34,7 @@ public ResponseEditorView( new HexCodeAreaFactory(logging, userInterface), collaboratorPayloadGenerator, new ErrorLoggingActionListenerFactory(logging), + signerConfig, editable, isProVersion ); diff --git a/src/main/java/one/d4d/sessionless/forms/SettingsView.form b/src/main/java/one/d4d/sessionless/forms/SettingsView.form index f924d97..5ace786 100644 --- a/src/main/java/one/d4d/sessionless/forms/SettingsView.form +++ b/src/main/java/one/d4d/sessionless/forms/SettingsView.form @@ -1,9 +1,9 @@
- + - + @@ -78,6 +78,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/one/d4d/sessionless/forms/SettingsView.java b/src/main/java/one/d4d/sessionless/forms/SettingsView.java index b7dc267..4c5e754 100644 --- a/src/main/java/one/d4d/sessionless/forms/SettingsView.java +++ b/src/main/java/one/d4d/sessionless/forms/SettingsView.java @@ -2,8 +2,9 @@ import burp.api.montoya.ui.UserInterface; import burp.config.BurpConfig; +import burp.config.SignerConfig; import burp.proxy.HighlightColor; -import burp.proxy.ProxyConfig; +import burp.config.ProxyConfig; import javax.swing.*; import java.awt.*; @@ -19,10 +20,18 @@ public class SettingsView { private JComboBox comboBoxHighlightColor; private JLabel labelHighlightToken; private JLabel labelHighlightColor; + private JPanel signerPanel; + private JLabel signerLabel; + private JCheckBox checkBoxEnableUnknownSignedString; + private JCheckBox checkBoxEnableDangerousSignedString; + private JCheckBox checkBoxEnableExpressSignedString; + private JCheckBox checkBoxEnableOAuthSignedString; + private JCheckBox checkBoxEnableTornadoSignedString; public SettingsView(Window parent, BurpConfig burpConfig, UserInterface userInterface) { this.parent = parent; ProxyConfig proxyConfig = burpConfig.proxyConfig(); + SignerConfig signerConfig = burpConfig.signerConfig(); checkBoxHighlightToken.setSelected(proxyConfig.highlightToken()); checkBoxHighlightToken.addActionListener(e -> { @@ -35,10 +44,34 @@ public SettingsView(Window parent, BurpConfig burpConfig, UserInterface userInte comboBoxHighlightColor.setEnabled(proxyConfig.highlightToken()); comboBoxHighlightColor.addActionListener(e -> proxyConfig.setHighlightColor((HighlightColor) comboBoxHighlightColor.getSelectedItem())); - proxyLabel.setFont(proxyLabel.getFont().deriveFont(BOLD)); userInterface.applyThemeToComponent(mainPanel); comboBoxHighlightColor.setRenderer(new HighlightComboRenderer()); + + checkBoxEnableDangerousSignedString.setSelected(signerConfig.isEnableDangerous()); + checkBoxEnableDangerousSignedString.addActionListener(e -> { + signerConfig.setEnableDangerous(checkBoxEnableDangerousSignedString.isSelected()); + }); + + checkBoxEnableExpressSignedString.setSelected(signerConfig.isEnableExpress()); + checkBoxEnableExpressSignedString.addActionListener(e -> { + signerConfig.setEnableExpress(checkBoxEnableExpressSignedString.isSelected()); + }); + + checkBoxEnableOAuthSignedString.setSelected(signerConfig.isEnableOAuth()); + checkBoxEnableOAuthSignedString.addActionListener(e -> { + signerConfig.setEnableOAuth(checkBoxEnableOAuthSignedString.isSelected()); + }); + + checkBoxEnableTornadoSignedString.setSelected(signerConfig.isEnableTornado()); + checkBoxEnableTornadoSignedString.addActionListener(e -> { + signerConfig.setEnableTornado(checkBoxEnableTornadoSignedString.isSelected()); + }); + + checkBoxEnableUnknownSignedString.setSelected(signerConfig.isEnableUnknown()); + checkBoxEnableUnknownSignedString.addActionListener(e -> { + signerConfig.setEnableUnknown(checkBoxEnableUnknownSignedString.isSelected()); + }); } private static class HighlightComboRenderer implements ListCellRenderer { diff --git a/src/main/java/one/d4d/sessionless/forms/dialog/AttackDialog.java b/src/main/java/one/d4d/sessionless/forms/dialog/AttackDialog.java index 2492dca..4623fa9 100644 --- a/src/main/java/one/d4d/sessionless/forms/dialog/AttackDialog.java +++ b/src/main/java/one/d4d/sessionless/forms/dialog/AttackDialog.java @@ -118,6 +118,8 @@ private void onOK() { s = new OauthProxyTokenSigner(selectedKey); } else if (tokenObject instanceof TornadoSignedToken) { s = new TornadoTokenSigner(selectedKey); + } else if (tokenObject instanceof UnknownSignedToken) { + s = new TokenSigner(selectedKey); } else { throw new Exception("Unknown"); } diff --git a/src/main/java/one/d4d/sessionless/forms/dialog/BruteForceAttackDialog.java b/src/main/java/one/d4d/sessionless/forms/dialog/BruteForceAttackDialog.java index 92f9ce2..b912940 100644 --- a/src/main/java/one/d4d/sessionless/forms/dialog/BruteForceAttackDialog.java +++ b/src/main/java/one/d4d/sessionless/forms/dialog/BruteForceAttackDialog.java @@ -25,6 +25,7 @@ public BruteForceAttackDialog( ErrorLoggingActionListenerFactory actionListenerFactory, List signingSecrets, List signingSalts, + List signingKeys, Attack mode, SignedToken token ) { @@ -56,16 +57,17 @@ public void actionPerformed(ActionEvent e) { }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); // Logic - lblStatus.setText(String.format( + String lblText = String.format( Utils.getResourceString("attack_dialog_progress_bar_status"), - signingSecrets.size(), - signingSalts.size(), + mode == Attack.KNOWN ? signingKeys.size() : signingSecrets.size(), + mode == Attack.KNOWN ? signingKeys.size() : signingSalts.size(), mode.getName() - )); + ); + lblStatus.setText(lblText); SwingWorker sw = new SwingWorker() { @Override protected Void doInBackground() throws Exception { - BruteForce bf = new BruteForce(signingSecrets, signingSalts, mode, token); + BruteForce bf = new BruteForce(signingSecrets, signingSalts, signingKeys, mode, token); SecretKey k = bf.search(); if (k != null) { secretKey = k; diff --git a/src/main/java/one/d4d/sessionless/forms/dialog/NewKeyDialog.form b/src/main/java/one/d4d/sessionless/forms/dialog/NewKeyDialog.form index 162d48b..23c501f 100644 --- a/src/main/java/one/d4d/sessionless/forms/dialog/NewKeyDialog.form +++ b/src/main/java/one/d4d/sessionless/forms/dialog/NewKeyDialog.form @@ -3,7 +3,7 @@ - + @@ -67,7 +67,7 @@ - + @@ -88,7 +88,7 @@ - + @@ -112,7 +112,7 @@ - + @@ -126,7 +126,7 @@ - + @@ -136,7 +136,7 @@ - + @@ -147,6 +147,30 @@ + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/one/d4d/sessionless/forms/dialog/NewKeyDialog.java b/src/main/java/one/d4d/sessionless/forms/dialog/NewKeyDialog.java index 9e6c97e..a425932 100644 --- a/src/main/java/one/d4d/sessionless/forms/dialog/NewKeyDialog.java +++ b/src/main/java/one/d4d/sessionless/forms/dialog/NewKeyDialog.java @@ -2,6 +2,7 @@ import one.d4d.sessionless.itsdangerous.Algorithms; import one.d4d.sessionless.itsdangerous.Derivation; +import one.d4d.sessionless.itsdangerous.MessageDerivation; import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; import one.d4d.sessionless.keys.Key; import one.d4d.sessionless.keys.SecretKey; @@ -29,6 +30,7 @@ public class NewKeyDialog extends KeyDialog { private JTextField textFieldKeyID; private JCheckBox checkBoxSecretJSON; private JCheckBox checkBoxSaltJSON; + private JComboBox comboBoxMessageDerivation; private SecretKey key; @@ -43,6 +45,7 @@ public NewKeyDialog(Window parent, PresenterStore presenters) { "", Algorithms.SHA1, Derivation.HMAC, + MessageDerivation.NONE, MessageDigestAlgorithm.SHA1); originalId = null; } @@ -58,6 +61,7 @@ public NewKeyDialog(Window parent, PresenterStore presenters, SecretKey key) { key.getSeparator(), key.getDigestMethod(), key.getKeyDerivation(), + key.getMessageDerivation(), key.getMessageDigestAlgorythm() ); } @@ -72,6 +76,7 @@ private NewKeyDialog( String separator, Algorithms algorithms, Derivation derivation, + MessageDerivation messageDerivation, MessageDigestAlgorithm digest) { super(parent, TITLE_RESOURCE_ID); this.presenters = presenters; @@ -109,6 +114,8 @@ private NewKeyDialog( comboBoxDigest.setSelectedItem(digest); comboBoxDerivation.setModel(new DefaultComboBoxModel<>(Derivation.values())); comboBoxDerivation.setSelectedItem(derivation); + comboBoxMessageDerivation.setModel(new DefaultComboBoxModel<>(MessageDerivation.values())); + comboBoxMessageDerivation.setSelectedItem(messageDerivation); } private void checkInput() { @@ -133,6 +140,7 @@ void onOK() { textFieldSeparator.getText(), (Algorithms) comboBoxAlgorythm.getSelectedItem(), (Derivation) comboBoxDerivation.getSelectedItem(), + (MessageDerivation) comboBoxMessageDerivation.getSelectedItem(), (MessageDigestAlgorithm) comboBoxDigest.getSelectedItem() ); super.onOK(); diff --git a/src/main/java/one/d4d/sessionless/forms/dialog/SignDialog.java b/src/main/java/one/d4d/sessionless/forms/dialog/SignDialog.java index 36e277f..9d23cf8 100644 --- a/src/main/java/one/d4d/sessionless/forms/dialog/SignDialog.java +++ b/src/main/java/one/d4d/sessionless/forms/dialog/SignDialog.java @@ -60,6 +60,8 @@ private void onOK() { s = new OauthProxyTokenSigner(selectedKey); } else if (tokenObject instanceof TornadoSignedToken) { s = new TornadoTokenSigner(selectedKey); + } else if (tokenObject instanceof UnknownSignedToken) { + s = new TokenSigner(selectedKey); } else { throw new Exception("Unknown"); } diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/Attack.java b/src/main/java/one/d4d/sessionless/itsdangerous/Attack.java index 4b0ebf2..082a86f 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/Attack.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/Attack.java @@ -3,6 +3,8 @@ import com.google.gson.annotations.SerializedName; public enum Attack { + @SerializedName("Known") + KNOWN("Known"), @SerializedName("Fast") FAST("Fast"), @SerializedName("Balanced") diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/BruteForce.java b/src/main/java/one/d4d/sessionless/itsdangerous/BruteForce.java index 3dfb558..c977e5b 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/BruteForce.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/BruteForce.java @@ -2,6 +2,7 @@ import one.d4d.sessionless.itsdangerous.crypto.TokenSigner; import one.d4d.sessionless.itsdangerous.model.SignedToken; +import one.d4d.sessionless.itsdangerous.model.UnknownSignedToken; import one.d4d.sessionless.keys.SecretKey; import java.util.ArrayList; @@ -10,12 +11,14 @@ public class BruteForce { private final List secrets; private final List salts; + private final List signingKeys; private final Attack scanConfiguration; private final SignedToken token; - public BruteForce(List secrets, List salts, Attack scanConfiguration, SignedToken token) { + public BruteForce(List secrets, List salts, List signingKeys, Attack scanConfiguration, SignedToken token) { this.secrets = secrets; this.salts = salts; + this.signingKeys = signingKeys; this.scanConfiguration = scanConfiguration; this.token = token; } @@ -23,6 +26,13 @@ public BruteForce(List secrets, List salts, Attack scanConfigura public List prepare() { List attacks = new ArrayList<>(); TokenSigner is = token.getSigner(); + if (scanConfiguration == Attack.KNOWN) { + this.signingKeys.forEach(key -> { + TokenSigner ks = new TokenSigner(key); + attacks.add(ks); + }); + return attacks; + } for (String secret : secrets) { if (scanConfiguration == Attack.FAST) { if (is.getKeyDerivation() == Derivation.NONE) { @@ -38,31 +48,37 @@ public List prepare() { } } } else { - for (Derivation d : Derivation.values()) { - if (d == Derivation.NONE) { - TokenSigner s = is.clone(); - s.setKeyDerivation(d); - s.setSecretKey(secret.getBytes()); - attacks.add(s); - } else if (d == Derivation.HASH ) { - for (MessageDigestAlgorithm m : MessageDigestAlgorithm.values()) { + for (MessageDerivation md : MessageDerivation.values()) { + if(md != MessageDerivation.NONE && !(token instanceof UnknownSignedToken)) continue; + for (Derivation d : Derivation.values()) { + if (d == Derivation.NONE) { TokenSigner s = is.clone(); s.setKeyDerivation(d); + s.setMessageDerivation(md); s.setSecretKey(secret.getBytes()); - s.setMessageDigestAlgorithm(m); attacks.add(s); - } - } else { - if (d == Derivation.PBKDF2HMAC && scanConfiguration != Attack.Deep) continue; - for (MessageDigestAlgorithm m : MessageDigestAlgorithm.values()) { - for (String salt : salts) { + } else if (d == Derivation.HASH) { + for (MessageDigestAlgorithm m : MessageDigestAlgorithm.values()) { TokenSigner s = is.clone(); s.setKeyDerivation(d); + s.setMessageDerivation(md); s.setSecretKey(secret.getBytes()); - s.setSalt(salt.getBytes()); s.setMessageDigestAlgorithm(m); attacks.add(s); } + } else { + if (d == Derivation.PBKDF2HMAC && scanConfiguration != Attack.Deep) continue; + for (MessageDigestAlgorithm m : MessageDigestAlgorithm.values()) { + for (String salt : salts) { + TokenSigner s = is.clone(); + s.setKeyDerivation(d); + s.setMessageDerivation(md); + s.setSecretKey(secret.getBytes()); + s.setSalt(salt.getBytes()); + s.setMessageDigestAlgorithm(m); + attacks.add(s); + } + } } } } diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/MessageDerivation.java b/src/main/java/one/d4d/sessionless/itsdangerous/MessageDerivation.java new file mode 100644 index 0000000..8298ba0 --- /dev/null +++ b/src/main/java/one/d4d/sessionless/itsdangerous/MessageDerivation.java @@ -0,0 +1,14 @@ +package one.d4d.sessionless.itsdangerous; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +public enum MessageDerivation { + @Expose @SerializedName("0") NONE("none"), + @Expose @SerializedName("1") CONCAT("concat"), + @Expose @SerializedName("2") TORNADO("tornado"); + public final String name; + MessageDerivation(String name) { + this.name = name; + } +} diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/DangerousTokenSigner.java b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/DangerousTokenSigner.java index 20df519..febc3ea 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/DangerousTokenSigner.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/DangerousTokenSigner.java @@ -1,10 +1,7 @@ package one.d4d.sessionless.itsdangerous.crypto; import com.google.common.primitives.Bytes; -import one.d4d.sessionless.itsdangerous.Algorithms; -import one.d4d.sessionless.itsdangerous.BadSignatureException; -import one.d4d.sessionless.itsdangerous.Derivation; -import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; +import one.d4d.sessionless.itsdangerous.*; import one.d4d.sessionless.keys.SecretKey; import one.d4d.sessionless.utils.Utils; @@ -31,11 +28,12 @@ public DangerousTokenSigner(Algorithms digestMethod, Derivation keyDerivation, b public DangerousTokenSigner( Algorithms algorithm, Derivation keyDerivation, + MessageDerivation messageDerivation, MessageDigestAlgorithm digest, byte[] secret_key, byte[] salt, byte sep) { - super(algorithm, keyDerivation, digest, secret_key, salt, sep); + super(algorithm, keyDerivation, messageDerivation, digest, secret_key, salt, sep); } public DangerousTokenSigner(String digestMethod, String keyDerivation, byte[] secret_key, byte[] salt, byte sep) { diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/DjangoTokenSigner.java b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/DjangoTokenSigner.java index a5c0117..e520892 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/DjangoTokenSigner.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/DjangoTokenSigner.java @@ -2,6 +2,7 @@ import one.d4d.sessionless.itsdangerous.Algorithms; import one.d4d.sessionless.itsdangerous.Derivation; +import one.d4d.sessionless.itsdangerous.MessageDerivation; import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; import one.d4d.sessionless.keys.SecretKey; @@ -29,10 +30,11 @@ public DjangoTokenSigner(String digestMethod, String keyDerivation, byte[] secre public DjangoTokenSigner( Algorithms algorithm, Derivation keyDerivation, + MessageDerivation messageDerivation, MessageDigestAlgorithm digest, byte[] secret_key, byte[] salt, byte sep) { - super(algorithm, keyDerivation, digest, secret_key, salt, sep); + super(algorithm, keyDerivation, messageDerivation, digest, secret_key, salt, sep); } } diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/ExpressTokenSigner.java b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/ExpressTokenSigner.java index 04f82f6..9856308 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/ExpressTokenSigner.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/ExpressTokenSigner.java @@ -13,7 +13,7 @@ public ExpressTokenSigner(SecretKey key) { } public ExpressTokenSigner() { - super(Algorithms.SHA1, Derivation.NONE, MessageDigestAlgorithm.NONE, new byte[]{}, new byte[]{}, (byte) 0); + super(Algorithms.SHA1, Derivation.NONE, MessageDerivation.NONE, MessageDigestAlgorithm.NONE, new byte[]{}, new byte[]{}, (byte) 0); } public ExpressTokenSigner(byte sep) { diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/JSONWebSignatureTokenSigner.java b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/JSONWebSignatureTokenSigner.java index 91b08d1..c0c907f 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/JSONWebSignatureTokenSigner.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/JSONWebSignatureTokenSigner.java @@ -2,6 +2,7 @@ import one.d4d.sessionless.itsdangerous.Algorithms; import one.d4d.sessionless.itsdangerous.Derivation; +import one.d4d.sessionless.itsdangerous.MessageDerivation; import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; import one.d4d.sessionless.keys.SecretKey; @@ -13,6 +14,6 @@ public JSONWebSignatureTokenSigner(SecretKey key) { } public JSONWebSignatureTokenSigner(byte sep) { - super(Algorithms.SHA256, Derivation.NONE, MessageDigestAlgorithm.NONE, new byte[]{}, new byte[]{}, sep); + super(Algorithms.SHA256, Derivation.NONE, MessageDerivation.NONE, MessageDigestAlgorithm.NONE, new byte[]{}, new byte[]{}, sep); } } diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/OauthProxyTokenSigner.java b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/OauthProxyTokenSigner.java index b6d76d3..fa53815 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/OauthProxyTokenSigner.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/OauthProxyTokenSigner.java @@ -1,9 +1,6 @@ package one.d4d.sessionless.itsdangerous.crypto; -import one.d4d.sessionless.itsdangerous.Algorithms; -import one.d4d.sessionless.itsdangerous.Derivation; -import one.d4d.sessionless.itsdangerous.DerivationException; -import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; +import one.d4d.sessionless.itsdangerous.*; import one.d4d.sessionless.utils.Utils; import one.d4d.sessionless.keys.SecretKey; import javax.crypto.Mac; @@ -15,10 +12,10 @@ public OauthProxyTokenSigner(SecretKey key) { super(key); } public OauthProxyTokenSigner() { - super(Algorithms.SHA256, Derivation.NONE, MessageDigestAlgorithm.NONE, new byte[] {}, new byte[] {}, (byte)'|'); + super(Algorithms.SHA256, Derivation.NONE, MessageDerivation.NONE, MessageDigestAlgorithm.NONE, new byte[] {}, new byte[] {}, (byte)'|'); } public OauthProxyTokenSigner(Algorithms digestMethod, byte[] secret_key, byte sep) { - super(digestMethod, Derivation.NONE, MessageDigestAlgorithm.NONE, secret_key, new byte[] {}, sep); + super(digestMethod, Derivation.NONE, MessageDerivation.NONE, MessageDigestAlgorithm.NONE, secret_key, new byte[] {}, sep); } @Override public byte[] derive_key() throws DerivationException { diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/TokenSigner.java b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/TokenSigner.java index 5c800a1..8d9d127 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/TokenSigner.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/TokenSigner.java @@ -21,6 +21,7 @@ public class TokenSigner implements Cloneable { public Algorithms digestMethod = Algorithms.SHA1; public Derivation keyDerivation = Derivation.HMAC; + public MessageDerivation messageDerivation = MessageDerivation.NONE; public MessageDigestAlgorithm messageDigestAlgorithm = MessageDigestAlgorithm.SHA1; public byte[] secret_key; public byte[] salt = "itsdangerous.Signer".getBytes(); @@ -40,6 +41,7 @@ public TokenSigner(byte[] secret_key, byte sep) { public TokenSigner(SecretKey key) { this.digestMethod = key.getDigestMethod(); this.keyDerivation = key.getKeyDerivation(); + this.messageDerivation = key.getMessageDerivation(); this.secret_key = key.getSecret().getBytes(); this.salt = key.getSalt().getBytes(); this.sep = key.getSeparator().getBytes().length > 0 ? key.getSeparator().getBytes()[0] : 46; @@ -54,9 +56,10 @@ public TokenSigner(Algorithms digestMethod, Derivation keyDerivation, byte[] sec this.sep = sep; } - public TokenSigner(Algorithms digestMethod, Derivation keyDerivation, MessageDigestAlgorithm digest, byte[] secret_key, byte[] salt, byte sep) { + public TokenSigner(Algorithms digestMethod, Derivation keyDerivation, MessageDerivation messageDerivation, MessageDigestAlgorithm digest, byte[] secret_key, byte[] salt, byte sep) { this.digestMethod = digestMethod; this.keyDerivation = keyDerivation; + this.messageDerivation = messageDerivation; this.messageDigestAlgorithm = digest; this.secret_key = secret_key; this.salt = salt; @@ -79,6 +82,14 @@ public void setKeyDerivation(Derivation keyDerivation) { this.keyDerivation = keyDerivation; } + public void setMessageDerivation(MessageDerivation messageDerivation) { + this.messageDerivation = messageDerivation; + } + + public MessageDerivation getMessageDerivation() { + return messageDerivation; + } + public MessageDigestAlgorithm getMessageDigestAlgorythm() { return messageDigestAlgorithm; } @@ -112,6 +123,19 @@ public void setMessageDigestAlgorithm(MessageDigestAlgorithm messageDigestAlgori this.messageDigestAlgorithm = messageDigestAlgorithm; } + public byte[] derive_message(byte[] value) { + switch (messageDerivation){ + case TORNADO -> { + return Bytes.concat(value, new byte[] {sep}); + } + case CONCAT -> { + return Bytes.concat(Utils.split(value, sep)); + } + default -> { + return value; + } + } + } public byte[] derive_key() throws DerivationException { try { if (messageDigestAlgorithm == MessageDigestAlgorithm.NONE) return secret_key; @@ -181,11 +205,12 @@ public byte[] get_signature_unsafe(byte[] value) throws Exception { public byte[] get_signature_bytes(byte[] value) { try { + byte[] message = derive_message(value); byte[] key = derive_key(); SecretKeySpec signingKey = new SecretKeySpec(key, digestMethod.name); Mac mac = Mac.getInstance(digestMethod.name); mac.init(signingKey); - return mac.doFinal(value); + return mac.doFinal(message); } catch (Exception e) { return new byte[]{}; } @@ -244,7 +269,7 @@ public SecretKey getKey() { String.valueOf((char) sep), digestMethod, keyDerivation, - messageDigestAlgorithm); + messageDerivation, messageDigestAlgorithm); } @Override @@ -253,6 +278,7 @@ public TokenSigner clone() { TokenSigner clone = (TokenSigner) super.clone(); clone.setDigestMethod(clone.getDigestMethod()); clone.setKeyDerivation(clone.getKeyDerivation()); + clone.setMessageDerivation(clone.getMessageDerivation()); clone.setMessageDigestAlgorithm(clone.getMessageDigestAlgorythm()); clone.setSep(clone.getSep()); clone.setSecretKey(clone.getSecretKey()); diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/TornadoTokenSigner.java b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/TornadoTokenSigner.java index 52c105c..2a0987b 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/crypto/TornadoTokenSigner.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/crypto/TornadoTokenSigner.java @@ -14,7 +14,7 @@ public TornadoTokenSigner(SecretKey key) { super(key); } public TornadoTokenSigner() { - super(Algorithms.SHA1, Derivation.NONE, MessageDigestAlgorithm.NONE,new byte[] {}, new byte[] {}, (byte) '|'); + super(Algorithms.SHA1, Derivation.NONE, MessageDerivation.NONE, MessageDigestAlgorithm.NONE,new byte[] {}, new byte[] {}, (byte) '|'); } public TornadoTokenSigner( byte sep) { super(new byte[] {}, sep); diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/model/DangerousSignedToken.java b/src/main/java/one/d4d/sessionless/itsdangerous/model/DangerousSignedToken.java index a182e8e..ca8d334 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/model/DangerousSignedToken.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/model/DangerousSignedToken.java @@ -28,6 +28,7 @@ public DangerousSignedToken( String signature, Algorithms algorithm, Derivation derivation, + MessageDerivation messageDerivation, MessageDigestAlgorithm digest) { super(String.format("%s%c%s", payload, separator, timestamp)); this.separator = separator; @@ -37,6 +38,7 @@ public DangerousSignedToken( this.signer = new DangerousTokenSigner( algorithm, derivation, + messageDerivation, digest, new byte[]{}, new byte[]{}, diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/model/DjangoSignedToken.java b/src/main/java/one/d4d/sessionless/itsdangerous/model/DjangoSignedToken.java index 17634b1..0380398 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/model/DjangoSignedToken.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/model/DjangoSignedToken.java @@ -2,13 +2,14 @@ import one.d4d.sessionless.itsdangerous.Algorithms; import one.d4d.sessionless.itsdangerous.Derivation; +import one.d4d.sessionless.itsdangerous.MessageDerivation; import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; import one.d4d.sessionless.utils.Utils; public class DjangoSignedToken extends DangerousSignedToken { public DjangoSignedToken(byte separator, String payload, String timestamp, String signature) { - super(separator, payload, timestamp, signature, Algorithms.SHA1, Derivation.DJANGO, MessageDigestAlgorithm.SHA1); + super(separator, payload, timestamp, signature, Algorithms.SHA1, Derivation.DJANGO, MessageDerivation.NONE, MessageDigestAlgorithm.SHA1); } @Override diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/model/SignedTokenObjectFinder.java b/src/main/java/one/d4d/sessionless/itsdangerous/model/SignedTokenObjectFinder.java index d0a575c..6c333ef 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/model/SignedTokenObjectFinder.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/model/SignedTokenObjectFinder.java @@ -2,6 +2,7 @@ import burp.api.montoya.http.message.Cookie; import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.config.SignerConfig; import com.google.common.base.CharMatcher; import one.d4d.sessionless.utils.Utils; import org.apache.commons.lang3.StringUtils; @@ -12,32 +13,63 @@ import java.util.stream.Collectors; public class SignedTokenObjectFinder { - public final static char[] SEPARATORS = {'.', ':', '#', '|'}; - public final static char[] ALL_SEPARATORS = {'.', ':', '!', '#', '$', '*', ';', '@', '|', '~'}; + public static final char[] SEPARATORS = {'.', ':', '#', '|'}; + public static final char[] ALL_SEPARATORS = {'.', ':', '!', '#', '$', '*', ';', '@', '|', '~'}; + private static final int[] SIGNATURES_LENGTH = {20, 28, 32, 48, 64}; private static final String SIGNED_PARAM = ".SIG"; // Regular expressions for JWS/JWE extraction private static final String BASE64_REGEX = "[A-Za-z0-9-_]"; private static final String SEPARATOR_REGEX = "[.:!#$*;@|~]"; - private static final String SIGNER_REGEX = String.format("\\.?%s*%s+%s*%s+%s+", BASE64_REGEX, SEPARATOR_REGEX, BASE64_REGEX, SEPARATOR_REGEX, BASE64_REGEX); //NON-NLS - private static final Pattern SIGNER_OBJECT_PATTERN = Pattern.compile(String.format("(%s)", SIGNER_REGEX)); //NON-NLS + private static final String SIGNER_REGEX = String.format("\\.?%s*%s+%s*%s+%s+", BASE64_REGEX, SEPARATOR_REGEX, BASE64_REGEX, SEPARATOR_REGEX, BASE64_REGEX); + private static final Pattern SIGNER_OBJECT_PATTERN = Pattern.compile(String.format("(%s)", SIGNER_REGEX)); + private static final String UNKNOWN_SIGNED_STRING_REGEXP = String.format("(%s*%s%s{26,86})",BASE64_REGEX, SEPARATOR_REGEX, BASE64_REGEX); + private static final Pattern UNKNOWN_SIGNED_STRING_PATTERN = Pattern.compile(UNKNOWN_SIGNED_STRING_REGEXP); - - public static boolean containsSignedTokenObjects(String text, List cookies, List params) { - List candidates = extractSignedTokenObjects(text, cookies, params); + public static boolean containsSignedTokenObjects(SignerConfig signerConfig, String text, List cookies, List params) { + List candidates = extractSignedTokenObjects(signerConfig, text, cookies, params); return candidates.size() > 0; } - public static List extractSignedTokenObjects(String text, List cookies, List params) { + public static List extractSignedTokenObjects(SignerConfig signerConfig, String text, List cookies, List params) { List signedTokensObjects = new ArrayList<>(); - Set candidates = findCandidateSignedTokenObjectsWithin(text); - - for (String candidate : candidates) { - parseToken(candidate) - .ifPresent(value -> - signedTokensObjects.add(new MutableSignedToken(candidate, value))); + Map cookiesToHashMap = convertCookiesToHashMap(cookies); + Map paramsToHashMap = convertParamsToHashMap(params); + if (signerConfig.isEnableDangerous()) { + Set tokenCandidates = findCandidateSignedTokenObjectsWithin(text); + for (String candidate : tokenCandidates) { + parseToken(candidate) + .ifPresent(value -> + signedTokensObjects.add(new MutableSignedToken(candidate, value))); + } + } + if (signerConfig.isEnableExpress()) { + signedTokensObjects.addAll(parseExpressSignedParams(cookiesToHashMap)); + signedTokensObjects.addAll(parseExpressSignedParams(paramsToHashMap)); + } + if(signerConfig.isEnableOAuth()) { + cookiesToHashMap.forEach((name,value) -> { + parseOauthProxySignedToken(name, value).ifPresent(v -> signedTokensObjects.add(new MutableSignedToken(value, v))); + }); + paramsToHashMap.forEach((name,value) -> { + parseOauthProxySignedToken(name, value).ifPresent(v -> signedTokensObjects.add(new MutableSignedToken(value, v))); + }); + } + if(signerConfig.isEnableTornado()) { + cookiesToHashMap.forEach((name,value) -> { + parseTornadoSignedToken(name, value).ifPresent(v -> signedTokensObjects.add(new MutableSignedToken(value, v))); + }); + paramsToHashMap.forEach((name,value) -> { + parseTornadoSignedToken(name, value).ifPresent(v -> signedTokensObjects.add(new MutableSignedToken(value, v))); + }); + } + if(signerConfig.isEnableUnknown()) { + Set stringCandidates = findCandidateUnknownSignedStringWithin(text); + for (String candidate : stringCandidates) { + parseUnknownSignedString(candidate) + .ifPresent(value -> + signedTokensObjects.add(new MutableSignedToken(candidate, value))); + } } - signedTokensObjects.addAll(parseSignedTokenWithinHashMap(convertCookiesToHashMap(cookies))); - signedTokensObjects.addAll(parseSignedTokenWithinHashMap(convertParamsToHashMap(params))); return signedTokensObjects; } @@ -54,6 +86,17 @@ public static Set findCandidateSignedTokenObjectsWithin(String text) { return strings; } + public static Set findCandidateUnknownSignedStringWithin(String text) { + Matcher m = UNKNOWN_SIGNED_STRING_PATTERN.matcher(text); + Set strings = new HashSet<>(); + + while (m.find()) { + String token = m.group(); + strings.add(token); + } + + return strings; + } public static Optional parseToken(String candidate) { Optional dst = parseDjangoSignedToken(candidate); return dst.isPresent() ? dst : parseDangerousSignedToken(candidate); @@ -83,6 +126,32 @@ private static Map convertCookiesToHashMap(List cookies) Cookie::value) ); } + public static List parseExpressSignedParams(Map params) { + List signedTokensObjects = new ArrayList<>(); + if (params != null) { + List signatures = params + .keySet() + .stream() + .filter(value -> value.toUpperCase().contains(SIGNED_PARAM)) + .toList(); + for (String signature : signatures) { + String sigValue = params.get(signature); + String signedParameter = signature.substring(0, signature.toUpperCase().indexOf(SIGNED_PARAM)); + if(params.get(signedParameter) == null) continue; + String signedValue = params.get(signedParameter); + try { + Base64.getUrlDecoder().decode(signedValue); + } catch (Exception e) { + continue; + } + ExpressSignedToken t = new ExpressSignedToken(signedParameter, signedValue, sigValue); + signedTokensObjects.add(new MutableSignedToken(signedValue, t)); + + } + } + + return signedTokensObjects; + } public static List parseSignedTokenWithinHashMap(Map params) { List signedTokensObjects = new ArrayList<>(); if (params != null) { @@ -283,9 +352,8 @@ private static Optional parseDangerousSignedToken(String text) { // Signature guesser String signature = parts[2]; try { - if (Utils.normalization(signature.getBytes()).length < 20) { - return Optional.empty(); - } + int length = Utils.normalization(signature.getBytes()).length; + if(Arrays.stream(SIGNATURES_LENGTH).noneMatch(x -> x == length)) return Optional.empty(); } catch (Exception e) { return Optional.empty(); } @@ -323,9 +391,8 @@ public static Optional parseDjangoSignedToken(String text) { // Signature guesser String signature = parts[2]; try { - if (Utils.normalization(signature.getBytes()).length < 20) { - return Optional.empty(); - } + int length = Utils.normalization(signature.getBytes()).length; + if(Arrays.stream(SIGNATURES_LENGTH).noneMatch(x -> x == length)) return Optional.empty(); } catch (Exception e) { return Optional.empty(); } @@ -366,7 +433,8 @@ public static Optional parseJSONWebSignature(String text) { // Signature parser String signature = parts[2]; try { - Utils.base64Decompress(signature.getBytes()); + int length = Utils.base64Decompress(signature.getBytes()).length; + if(Arrays.stream(SIGNATURES_LENGTH).noneMatch(x -> x == length)) return Optional.empty(); } catch (Exception e) { return Optional.empty(); } @@ -374,4 +442,26 @@ public static Optional parseJSONWebSignature(String text) { return Optional.of(t); } + + public static Optional parseUnknownSignedString(String text) { + char separator = 0; + for (char sep : SEPARATORS) { + if (CharMatcher.is(sep).countIn(text) > 0) { + separator = sep; + } + } + if (separator == 0) return Optional.empty(); + int index = text.lastIndexOf(separator); + String message = text.substring(0,index); + String signature = text.substring(index + 1); + try { + int length = Utils.normalization(signature.getBytes()).length; + if(Arrays.stream(SIGNATURES_LENGTH).noneMatch(x -> x == length)) return Optional.empty(); + } catch (Exception e) { + return Optional.empty(); + } + + UnknownSignedToken t = new UnknownSignedToken(message, signature, (byte) separator); + return Optional.of(t); + } } diff --git a/src/main/java/one/d4d/sessionless/itsdangerous/model/UnknownSignedToken.java b/src/main/java/one/d4d/sessionless/itsdangerous/model/UnknownSignedToken.java index 2338eae..d8a08a2 100644 --- a/src/main/java/one/d4d/sessionless/itsdangerous/model/UnknownSignedToken.java +++ b/src/main/java/one/d4d/sessionless/itsdangerous/model/UnknownSignedToken.java @@ -1,6 +1,10 @@ package one.d4d.sessionless.itsdangerous.model; import com.nimbusds.jwt.JWTClaimsSet; +import one.d4d.sessionless.itsdangerous.Algorithms; +import one.d4d.sessionless.itsdangerous.Derivation; +import one.d4d.sessionless.itsdangerous.MessageDerivation; +import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; import one.d4d.sessionless.itsdangerous.crypto.TokenSigner; public class UnknownSignedToken extends SignedToken { @@ -10,8 +14,15 @@ public UnknownSignedToken(String message, String signature, byte separator) { super(message); this.signature = signature; this.separator = separator; - this.signer = new TokenSigner(new byte[]{}, separator); - } + this.signer = new TokenSigner( + Algorithms.SHA256, + Derivation.NONE, + MessageDerivation.NONE, + MessageDigestAlgorithm.NONE, + new byte[] {}, + new byte[] {}, + separator); + } @Override @@ -28,4 +39,7 @@ public void resign() throws Exception { public void setClaims(JWTClaimsSet claims) { } + public byte[] getSeparator() { + return new byte[]{separator}; + } } diff --git a/src/main/java/one/d4d/sessionless/keys/SecretKey.java b/src/main/java/one/d4d/sessionless/keys/SecretKey.java index d5e4488..a2cc9f2 100644 --- a/src/main/java/one/d4d/sessionless/keys/SecretKey.java +++ b/src/main/java/one/d4d/sessionless/keys/SecretKey.java @@ -4,6 +4,7 @@ import com.google.gson.annotations.SerializedName; import one.d4d.sessionless.itsdangerous.Algorithms; import one.d4d.sessionless.itsdangerous.Derivation; +import one.d4d.sessionless.itsdangerous.MessageDerivation; import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; public class SecretKey implements Key { @@ -26,6 +27,9 @@ public class SecretKey implements Key { @SerializedName("keyDerivation") private final Derivation keyDerivation; @Expose + @SerializedName("messageDerivation") + private final MessageDerivation messageDerivation; + @Expose @SerializedName("messageDigestAlgorythm") private final MessageDigestAlgorithm messageDigestAlgorithm; @@ -36,6 +40,7 @@ public SecretKey( String separator, Algorithms digestMethod, Derivation keyDerivation, + MessageDerivation messageDerivation, MessageDigestAlgorithm messageDigestAlgorithm) { this.keyId = keyId; this.secret = secret; @@ -43,6 +48,7 @@ public SecretKey( this.separator = separator; this.digestMethod = digestMethod; this.keyDerivation = keyDerivation; + this.messageDerivation = messageDerivation; this.messageDigestAlgorithm = messageDigestAlgorithm; } @@ -66,6 +72,10 @@ public Derivation getKeyDerivation() { return keyDerivation; } + public MessageDerivation getMessageDerivation() { + return messageDerivation; + } + public MessageDigestAlgorithm getMessageDigestAlgorythm() { return messageDigestAlgorithm; } diff --git a/src/main/java/one/d4d/sessionless/presenter/EditorModel.java b/src/main/java/one/d4d/sessionless/presenter/EditorModel.java index 0fb2420..32ee1cd 100644 --- a/src/main/java/one/d4d/sessionless/presenter/EditorModel.java +++ b/src/main/java/one/d4d/sessionless/presenter/EditorModel.java @@ -2,6 +2,7 @@ import burp.api.montoya.http.message.Cookie; import burp.api.montoya.http.message.params.ParsedHttpParameter; +import burp.config.SignerConfig; import one.d4d.sessionless.itsdangerous.model.MutableSignedToken; import one.d4d.sessionless.itsdangerous.model.SignedTokenObjectFinder; import org.apache.commons.lang3.StringUtils; @@ -11,6 +12,7 @@ import java.util.concurrent.atomic.AtomicInteger; class EditorModel { + private final SignerConfig signerConfig; private static final String SERIALIZED_OBJECT_FORMAT_STRING = "%d - %s"; private final List mutableSerializedObjects = new ArrayList<>(); @@ -18,11 +20,15 @@ class EditorModel { private String message; + public EditorModel(SignerConfig signerConfig) { + this.signerConfig = signerConfig; + } + void setMessage(String content, List cookies, List params) { synchronized (lock) { message = content; mutableSerializedObjects.clear(); - mutableSerializedObjects.addAll(SignedTokenObjectFinder.extractSignedTokenObjects(content,cookies,params)); + mutableSerializedObjects.addAll(SignedTokenObjectFinder.extractSignedTokenObjects(signerConfig,content,cookies,params)); } } diff --git a/src/main/java/one/d4d/sessionless/presenter/EditorPresenter.java b/src/main/java/one/d4d/sessionless/presenter/EditorPresenter.java index f1ca421..5374058 100644 --- a/src/main/java/one/d4d/sessionless/presenter/EditorPresenter.java +++ b/src/main/java/one/d4d/sessionless/presenter/EditorPresenter.java @@ -3,7 +3,7 @@ import burp.api.montoya.collaborator.CollaboratorPayloadGenerator; import burp.api.montoya.http.message.Cookie; import burp.api.montoya.http.message.params.ParsedHttpParameter; -import com.nimbusds.jwt.JWTClaimsSet; +import burp.config.SignerConfig; import one.d4d.sessionless.forms.EditorTab; import one.d4d.sessionless.forms.MessageDialogFactory; import one.d4d.sessionless.forms.dialog.*; @@ -22,6 +22,7 @@ public class EditorPresenter extends Presenter { private final PresenterStore presenters; + private final SignerConfig signerConfig; private final EditorModel model; private final EditorTab view; private final CollaboratorPayloadGenerator collaboratorPayloadGenerator; @@ -34,14 +35,16 @@ public EditorPresenter( EditorTab view, CollaboratorPayloadGenerator collaboratorPayloadGenerator, ErrorLoggingActionListenerFactory actionListenerFactory, - PresenterStore presenters) { + PresenterStore presenters, + SignerConfig signerConfig) { this.view = view; - this.model = new EditorModel(); + this.model = new EditorModel(signerConfig); this.collaboratorPayloadGenerator = collaboratorPayloadGenerator; this.actionListenerFactory = actionListenerFactory; this.presenters = presenters; messageDialogFactory = new MessageDialogFactory(view.uiComponent()); presenters.register(this); + this.signerConfig = signerConfig; } public void setMessage(String content, URL targetURL, List cookies, List params) { @@ -158,6 +161,20 @@ private void setTornado(TornadoSignedToken token) { view.setTornadoValue(token.getValue()); view.setTornadoSignature(token.getSignature()); } + private UnknownSignedToken getUnknown() { + + String message = view.getUnknownMessage(); + String signature = view.getUnknownSignature(); + byte[] separator = view.getUnknownSeparator().length == 0 ? new byte[]{46} : view.getUnknownSeparator(); + + return new UnknownSignedToken(message,signature,separator[0]); + } + + private void setUnknown(UnknownSignedToken token) { + view.setUnknownMessage(token.getEncodedMessage()); + view.setUnknownSignature(token.getEncodedSignature()); + view.setUnknownSeparator(token.getSeparator()); + } public void componentChanged() { @@ -169,23 +186,7 @@ public void componentChanged() { case EditorTab.TAB_EXPRESS -> tokenObject = getExpress(); case EditorTab.TAB_OAUTH -> tokenObject = getOAuth(); case EditorTab.TAB_TORNADO -> tokenObject = getTornado(); - default -> tokenObject = new SignedToken("Blank") { - @Override - public String serialize() { - return null; - } - - - @Override - public void resign() throws Exception { - - } - - @Override - public void setClaims(JWTClaimsSet claims) { - - } - }; + default -> tokenObject = getUnknown(); } mutableSignedTokenObject.setModified(tokenObject); view.setSignedToken(tokenObject.serialize(), mutableSignedTokenObject.changed() && !selectionChanging); @@ -209,6 +210,9 @@ public void onSelectionChanged() { } else if (tokenObject instanceof TornadoSignedToken) { view.setTornadoMode(); setTornado((TornadoSignedToken) tokenObject); + } else if (tokenObject instanceof UnknownSignedToken) { + view.setUnknownMode(); + setUnknown((UnknownSignedToken) tokenObject); } selectionChanging = false; } @@ -258,6 +262,9 @@ private void attackDialog() { } else if (signed instanceof TornadoSignedToken) { view.setTornadoMode(); setTornado((TornadoSignedToken) signed); + } else if (signed instanceof UnknownSignedToken) { + view.setUnknownMode(); + setUnknown((UnknownSignedToken) signed); } } } @@ -294,6 +301,9 @@ private void signingDialog() { } else if (signed instanceof TornadoSignedToken) { view.setTornadoMode(); setTornado((TornadoSignedToken) signed); + } else if (signed instanceof UnknownSignedToken) { + view.setUnknownMode(); + setUnknown((UnknownSignedToken) signed); } } } @@ -322,6 +332,7 @@ public void onAttackClicked(Attack mode) { actionListenerFactory, attackKeys, attackSalts, + keysPresenter.getSigningKeys(), mode, tokenObject); bruteForceDialog.display(); @@ -340,6 +351,9 @@ public void onAttackClicked(Attack mode) { } } + public void onAttackKnownKeysClicked() { + onAttackClicked(Attack.KNOWN); + } public void onAttackFastClicked() { onAttackClicked(Attack.FAST); } @@ -353,7 +367,7 @@ public void onAttackDeepClicked() { } public boolean isEnabled(String text, List cookies, List params) { - return containsSignedTokenObjects(text, cookies, params); + return containsSignedTokenObjects(signerConfig, text, cookies, params); } public String getMessage() { diff --git a/src/main/resources/strings.properties b/src/main/resources/strings.properties index 66723ee..dfe5386 100644 --- a/src/main/resources/strings.properties +++ b/src/main/resources/strings.properties @@ -33,6 +33,7 @@ error_title_no_secrets=Secrets not found! error_no_secrets=Please add secrets at settings tab error_title_no_salts=Salts not found! error_no_salts=Please add salts at settings tab +editor_view_button_attack_known_keys=Known keys editor_view_button_attack_fast=Fast editor_view_button_attack_balanced=Balanced editor_view_button_attack_deep=Deep @@ -55,6 +56,7 @@ table_id=ID table_secret=Secret table_algorithm=Algorithm table_derivation=Derivation +table_message_derivation=Message Derivation table_digest=Digest table_menu_delete=Delete table_menu_copy=Copy @@ -83,4 +85,13 @@ checkbox_user_access_token=User access_token panel_signing_keys_label=Signing keys checkbox_json_label=JSON button_sign_label=Sign -text_area_syntax_label=text/json \ No newline at end of file +text_area_syntax_label=text/json +unknown_tab_lable=Unknown +unknown_message_label=Message +unknown_signature_label=Signature +unknown_separator_label=Separator +signer_settings_label=Enabled signers: +key_dialog_digest=Digest +key_dialog_message_derivation=Message derivation +key_dialog_key_derivation=Key derivation +key_dialog_algorythm=Algorythm \ No newline at end of file diff --git a/src/test/java/BruteForceTest.java b/src/test/java/BruteForceTest.java index 7c599e2..d216b4a 100644 --- a/src/test/java/BruteForceTest.java +++ b/src/test/java/BruteForceTest.java @@ -7,6 +7,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -22,8 +23,9 @@ void BruteForceAttack() { token.setSigner(s); final List secrets = List.of("secret"); final List salts = List.of("salt"); + final List knownKeys = new ArrayList<>(); - BruteForce bf = new BruteForce(secrets, salts, Attack.FAST, token); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.FAST, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } else { @@ -42,8 +44,9 @@ void BruteForceMultiThreatAttack() { token.setSigner(s); final List secrets = List.of("secret"); final List salts = List.of("salt"); + final List knownKeys = new ArrayList<>(); - BruteForce bf = new BruteForce(secrets, salts, Attack.FAST, token); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.FAST, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } else { diff --git a/src/test/java/ClaimsTest.java b/src/test/java/ClaimsTest.java index 76eeb4a..7f1fb15 100644 --- a/src/test/java/ClaimsTest.java +++ b/src/test/java/ClaimsTest.java @@ -1,6 +1,7 @@ import com.nimbusds.jwt.JWTClaimsSet; import one.d4d.sessionless.itsdangerous.Algorithms; import one.d4d.sessionless.itsdangerous.Derivation; +import one.d4d.sessionless.itsdangerous.MessageDerivation; import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; import one.d4d.sessionless.keys.SecretKey; import one.d4d.sessionless.utils.ClaimsUtils; @@ -69,7 +70,15 @@ void AccountUserPayloadTest() { @Test void UserAccessTokenPayloadTest() { try { - SecretKey key = new SecretKey("1", "secret", "",".", Algorithms.SHA256, Derivation.NONE, MessageDigestAlgorithm.NONE); + SecretKey key = new SecretKey( + "1", + "secret", + "", + ".", + Algorithms.SHA256, + Derivation.NONE, + MessageDerivation.NONE, + MessageDigestAlgorithm.NONE); URL target = new URL("https://d4d.one/"); ClaimsUtils.generateUserAccessTokenPayload(target, key); } catch (MalformedURLException e) { diff --git a/src/test/java/CompressTest.java b/src/test/java/CompressTest.java index b51a257..55e89e7 100644 --- a/src/test/java/CompressTest.java +++ b/src/test/java/CompressTest.java @@ -15,6 +15,5 @@ void Base64EncodedTimestampTest() { }catch (Exception e) { fail(e.getMessage()); } - } } diff --git a/src/test/java/DjangoTest.java b/src/test/java/DjangoTest.java index 549f34c..f955fe9 100644 --- a/src/test/java/DjangoTest.java +++ b/src/test/java/DjangoTest.java @@ -11,6 +11,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -20,12 +21,13 @@ public class DjangoTest { void DjangoBruteForceTest() { List signingSecrets = List.of("secret"); List signingSalts = List.of("django.contrib.sessions.backends.signed_cookies"); + List knownKeys = new ArrayList<>(); Attack mode = Attack.Deep; String value = "gAWVMwAAAAAAAAB9lIwKdGVzdGNvb2tpZZSMBXBvc2l4lIwGc3lzdGVtlJOUjAhzbGVlcCAzMJSFlFKUcy4:1rBDnz:6RroyItcbm4P82lx2kEAuV2ykxs"; Optional optionalToken = SignedTokenObjectFinder.parseToken(value); if (optionalToken.isPresent()) { DangerousSignedToken token = (DangerousSignedToken) optionalToken.get(); - BruteForce bf = new BruteForce(signingSecrets, signingSalts, mode, token); + BruteForce bf = new BruteForce(signingSecrets, signingSalts, knownKeys, mode, token); SecretKey k = bf.search(); Assertions.assertNotNull(k); } else { diff --git a/src/test/java/JSONWebSignatureTest.java b/src/test/java/JSONWebSignatureTest.java index d2f717b..f9717f7 100644 --- a/src/test/java/JSONWebSignatureTest.java +++ b/src/test/java/JSONWebSignatureTest.java @@ -9,6 +9,7 @@ import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -17,11 +18,12 @@ public class JSONWebSignatureTest { void JSONWebSignatureParserTest() { final List secrets = List.of("your-256-bit-secret"); final List salts = List.of("salt"); + final List knownKeys = new ArrayList<>(); String value = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; Optional optionalToken = SignedTokenObjectFinder.parseJSONWebSignature(value); if (optionalToken.isPresent()) { JSONWebSignature token = (JSONWebSignature) optionalToken.get(); - BruteForce bf = new BruteForce(secrets, salts, Attack.FAST, token); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.FAST, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } else { @@ -33,13 +35,14 @@ void JSONWebSignatureClaimsTest() { try { final List secrets = List.of("secret"); final List salts = List.of("salt"); + final List knownKeys = new ArrayList<>(); URL target = new URL("https://d4d.one/"); - SecretKey key = new SecretKey("1", "secret", "",".", Algorithms.SHA256, Derivation.NONE, MessageDigestAlgorithm.NONE); + SecretKey key = new SecretKey("1", "secret", "",".", Algorithms.SHA256, Derivation.NONE, MessageDerivation.NONE, MessageDigestAlgorithm.NONE); String value = ClaimsUtils.generateJSONWebToken(target, ClaimsUtils.DEFAULT_USERNAME, key); Optional optionalToken = SignedTokenObjectFinder.parseJSONWebSignature(value); if (optionalToken.isPresent()) { JSONWebSignature token = (JSONWebSignature) optionalToken.get(); - BruteForce bf = new BruteForce(secrets, salts, Attack.FAST, token); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.FAST, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } else { diff --git a/src/test/java/KeyPersistenceStoreTest.java b/src/test/java/KeyPersistenceStoreTest.java index 8f43741..274ced7 100644 --- a/src/test/java/KeyPersistenceStoreTest.java +++ b/src/test/java/KeyPersistenceStoreTest.java @@ -2,6 +2,7 @@ import com.google.gson.Gson; import one.d4d.sessionless.itsdangerous.Algorithms; import one.d4d.sessionless.itsdangerous.Derivation; +import one.d4d.sessionless.itsdangerous.MessageDerivation; import one.d4d.sessionless.itsdangerous.MessageDigestAlgorithm; import one.d4d.sessionless.keys.SecretKey; import one.d4d.sessionless.utils.GsonHelper; @@ -16,7 +17,7 @@ void KeyDerivationTest() { KeysModel model = new KeysModel(); model.setSalts(Utils.readResourceForClass("/salts", this.getClass())); model.setSecrets(Utils.readResourceForClass("/secrets", this.getClass())); - model.addKey(new SecretKey("test", "secret","salt",".", Algorithms.SHA1, Derivation.HMAC, MessageDigestAlgorithm.SHA1)); + model.addKey(new SecretKey("test", "secret","salt",".", Algorithms.SHA1, Derivation.HMAC, MessageDerivation.NONE, MessageDigestAlgorithm.SHA1)); Gson gson = GsonHelper.customGson; String serial = gson.toJson(model); diff --git a/src/test/java/SignUnsignTest.java b/src/test/java/SignUnsignTest.java index 70f4567..1c4dcc0 100644 --- a/src/test/java/SignUnsignTest.java +++ b/src/test/java/SignUnsignTest.java @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -30,8 +31,9 @@ void KeyDerivationTest() { token.setSigner(s); final List secrets = List.of("secret"); final List salts = List.of("cookie-session"); + final List knownKeys = new ArrayList<>(); - BruteForce bf = new BruteForce(secrets, salts, Attack.Deep, token); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.Deep, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } diff --git a/src/test/java/TornadoTest.java b/src/test/java/TornadoTest.java index adf8b30..2288fcc 100644 --- a/src/test/java/TornadoTest.java +++ b/src/test/java/TornadoTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -40,8 +41,9 @@ void BruteForceMultiThreatTornado() { optionalToken.get().setSigner(s); final List secrets = Utils.readResourceForClass("/secrets", this.getClass()); final List salts = Utils.readResourceForClass("/salts", this.getClass()); + final List knownKeys = new ArrayList<>(); - BruteForce bf = new BruteForce(secrets, salts, Attack.FAST, optionalToken.get()); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.FAST, optionalToken.get()); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } diff --git a/src/test/java/UnknownSignedTokenTest.java b/src/test/java/UnknownSignedTokenTest.java index 5d78bcb..4c033f8 100644 --- a/src/test/java/UnknownSignedTokenTest.java +++ b/src/test/java/UnknownSignedTokenTest.java @@ -1,13 +1,17 @@ import com.google.common.collect.Lists; import one.d4d.sessionless.itsdangerous.*; import one.d4d.sessionless.itsdangerous.crypto.DjangoTokenSigner; +import one.d4d.sessionless.itsdangerous.model.SignedToken; +import one.d4d.sessionless.itsdangerous.model.SignedTokenObjectFinder; import one.d4d.sessionless.itsdangerous.model.UnknownSignedToken; import one.d4d.sessionless.keys.SecretKey; import one.d4d.sessionless.utils.Utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; public class UnknownSignedTokenTest { @Test @@ -19,7 +23,7 @@ void DjangoSignerTest() { "eyJtZXNzYWdlIjoiSGVsbG8hIn0", "V1O2qShdoisLMx2d0JTmVQecu8zsLPeXmTM5Id3ll", (byte)':'); - DjangoTokenSigner s = new DjangoTokenSigner(Algorithms.SHA256, Derivation.DJANGO, MessageDigestAlgorithm.SHA256,secret, salt, (byte) ':'); + DjangoTokenSigner s = new DjangoTokenSigner(Algorithms.SHA256, Derivation.DJANGO, MessageDerivation.NONE, MessageDigestAlgorithm.SHA256,secret, salt, (byte) ':'); token.setSigner(s); Assertions.assertDoesNotThrow( ()-> { s.unsign(value.getBytes()); @@ -35,8 +39,9 @@ void DjangoSignedMessageSaltDictionaryTest() { (byte)':'); final List secrets = Utils.readResourceForClass("/secrets", this.getClass()); final List salts = Utils.readResourceForClass("/salts", this.getClass()); + final List knownKeys = new ArrayList<>(); secrets.add(secret); - BruteForce bf = new BruteForce(secrets, salts, Attack.Balanced, token); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.Balanced, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } @@ -49,8 +54,9 @@ void DjangoSignedMessageBruteForceTest() { (byte)':'); final List secrets = Utils.readResourceForClass("/secrets", this.getClass()); final List salts = Utils.readResourceForClass("/salts", this.getClass()); + final List knownKeys = new ArrayList<>(); secrets.add(secret); - BruteForce bf = new BruteForce(secrets, salts, Attack.Balanced, token); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.Balanced, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } @@ -62,7 +68,8 @@ void URLSafeSerializerTest() { (byte)'.'); final List secrets = Lists.newArrayList("secret key"); final List salts = Lists.newArrayList("auth"); - BruteForce bf = new BruteForce(secrets, salts, Attack.Balanced, token); + final List knownKeys = new ArrayList<>(); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.Balanced, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } @@ -74,7 +81,8 @@ void ItsDangerousSignerTest() { (byte)'.'); final List secrets = Lists.newArrayList("secret-key"); final List salts = Lists.newArrayList("itsdangerous.Signer"); - BruteForce bf = new BruteForce(secrets, salts, Attack.Balanced, token); + final List knownKeys = new ArrayList<>(); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.Balanced, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } @@ -86,8 +94,25 @@ void RedashURLSafeTimedSerializerTest() { (byte)'.'); final List secrets = Lists.newArrayList("c292a0a3aa32397cdb050e233733900f"); final List salts = Lists.newArrayList("itsdangerous"); - BruteForce bf = new BruteForce(secrets, salts, Attack.Balanced, token); + final List knownKeys = new ArrayList<>(); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.Balanced, token); SecretKey sk = bf.search(); Assertions.assertNotNull(sk); } + @Test + void UnknownSignedStringParserTest() { + final List secrets = List.of("c292a0a3aa32397cdb050e233733900f"); + final List salts = List.of("itsdangerous"); + final List knownKeys = new ArrayList<>(); + String value = "IjEi.YhAmmQ.cdQp7CnnVq02aQ05y8tSBddl-qs"; + Optional optionalToken = SignedTokenObjectFinder.parseUnknownSignedString(value); + if (optionalToken.isPresent()) { + UnknownSignedToken token = (UnknownSignedToken) optionalToken.get(); + BruteForce bf = new BruteForce(secrets, salts, knownKeys, Attack.Balanced, token); + SecretKey sk = bf.search(); + Assertions.assertNotNull(sk); + } else { + Assertions.fail("Token not found."); + } + } }