├── .classpath
├── .github
└── workflows
│ └── maven.yml
├── .gitignore
├── .project
├── BappDescription.html
├── BappManifest.bmf
├── README.md
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── hackvertor.iml
├── settings.gradle
├── src
├── main
│ ├── java
│ │ ├── META-INF
│ │ │ └── MANIFEST.MF
│ │ └── burp
│ │ │ └── hv
│ │ │ ├── AES.java
│ │ │ ├── Base58.java
│ │ │ ├── Burp.java
│ │ │ ├── Convertors.java
│ │ │ ├── Hackvertor.java
│ │ │ ├── HackvertorAction.java
│ │ │ ├── HackvertorExtension.java
│ │ │ ├── HackvertorPayloadProcessor.java
│ │ │ ├── HttpListener.java
│ │ │ ├── Ngrams.java
│ │ │ ├── RequestDiffer.java
│ │ │ ├── Variables.java
│ │ │ ├── ai
│ │ │ ├── AI.java
│ │ │ ├── CodeConversion.java
│ │ │ ├── ExtractFunctionBody.java
│ │ │ ├── LearnFromRepeater.java
│ │ │ ├── LimitedHashMap.java
│ │ │ ├── PromptGenerator.java
│ │ │ ├── RemoveCodeReferences.java
│ │ │ └── SummariseCode.java
│ │ │ ├── settings
│ │ │ ├── InvalidTypeSettingException.java
│ │ │ ├── Settings.java
│ │ │ └── UnregisteredSettingException.java
│ │ │ ├── tags
│ │ │ ├── CustomTags.java
│ │ │ ├── Tag.java
│ │ │ ├── TagArgument.java
│ │ │ └── TagStore.java
│ │ │ ├── ui
│ │ │ ├── ContextMenu.java
│ │ │ ├── ExtensionPanel.java
│ │ │ ├── HackvertorInput.java
│ │ │ ├── HackvertorMessageTab.java
│ │ │ ├── HackvertorPanel.java
│ │ │ ├── JTabbedPaneClosable.java
│ │ │ ├── MenuScroller.java
│ │ │ ├── MontoyaContextMenu.java
│ │ │ ├── SearchPanel.java
│ │ │ └── SmartDecodeAction.java
│ │ │ └── utils
│ │ │ ├── GridbagUtils.java
│ │ │ ├── HttpUtils.java
│ │ │ ├── TagUtils.java
│ │ │ ├── UrlUtils.java
│ │ │ └── Utils.java
│ ├── javacc
│ │ └── burp
│ │ │ └── parser
│ │ │ ├── Element.java
│ │ │ └── parser.jj
│ └── resources
│ │ ├── bigrams.txt
│ │ ├── images
│ │ ├── logo-dark.png
│ │ ├── logo-light.png
│ │ └── logo.gif
│ │ ├── monograms.txt
│ │ ├── quadgrams.txt
│ │ └── trigrams.txt
└── test
│ └── java
│ ├── TestExtension.java
│ └── burp
│ ├── ConvertorTests.java
│ ├── parser
│ └── HackvertorParserTest.java
│ └── stubs
│ ├── StubCallbacks.java
│ └── StubExtensionHelpers.java
└── tag-store
├── README.md
├── base62_decode
└── base62_decode.py
├── base62_encode
└── base62_encode.py
├── ean13
└── ean13.py
├── email_utf7
└── email_utf7.groovy
├── email_utf7_decode
└── email_utf7_decode.groovy
├── encode_word_meta
└── encode_word_meta.js
├── encoded_word_decode
└── encoded_word_decode.js
├── encoded_word_encode
└── encoded_word_encode.js
├── hello_world
└── hello_world.py
├── ip
└── ip.js
├── tag-store.json
├── totp
└── totp.py
├── unicode_overflow
└── unicode_overflow.js
├── unicode_overflow_variations
└── unicode_overflow_variations.js
└── uuid
└── uuid.py
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.github/workflows/maven.yml:
--------------------------------------------------------------------------------
1 | # This workflow will build a Java project with Maven
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
3 |
4 | name: Java CI with Maven
5 |
6 | on:
7 | push:
8 | tags:
9 | - '*'
10 |
11 | jobs:
12 | build:
13 |
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v3
18 | - name: Set up JDK 17
19 | uses: actions/setup-java@v3
20 | with:
21 | distribution: 'oracle'
22 | java-version: '17'
23 | cache: 'gradle'
24 | - name: Grant execute permission for gradlew
25 | run: chmod +x gradlew
26 | - name: Build with Gradle
27 | run: ./gradlew build
28 | - name: Creating the jar file
29 | run: ./gradlew jar
30 | - name: Upload artifact
31 | uses: actions/upload-artifact@v3
32 | with:
33 | path: ./releases/*.jar
34 | name: Downloadable Extension File
35 | - name: Release
36 | uses: hackvertor/release-action@v1.12.0
37 | with:
38 | name: ${{github.ref_name}}
39 | allowUpdates: true
40 | artifacts: "releases/*.jar"
41 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | #Build directories
3 | bin/
4 | build/
5 | target/
6 | out/
7 | /releases/
8 | #intellij
9 | .idea/
10 | .classpath/
11 | .project/
12 | /build/
13 | /.gradle/
14 | libs/montoya-api-2025.3.6.jar
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | hackvertor
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 |
15 | org.eclipse.jdt.core.javanature
16 |
17 |
18 |
--------------------------------------------------------------------------------
/BappDescription.html:
--------------------------------------------------------------------------------
1 |
2 | Hackvertor is a powerful, versatile tool designed to supercharge your workflows by seamlessly converting, encoding, and transforming text or code. Whether you're a developer, hacker, or tech enthusiast, Hackvertor empowers you with advanced features like custom scripting, real-time preview, and an extensive library of transformations. It's your ultimate sidekick for tackling complex encoding challenges, optimizing processes, and unleashing creativity with precision and speed.
3 |
4 |
5 | How to use
6 |
7 |
8 | - Navigate to the Hackvertor tab and enter some text in the input window
9 | - Make a selection of the text, and select one of the tags from the tag menu at the top
10 | - Click on of the tags to add it to the input
11 | - Hackvertor will convert the tags in the input and show the converted output
12 |
13 |
14 | Hackvertor AI features
15 |
16 | Use AI to generate code
17 |
18 | This feature allows a user to generate custom tags in JavaScript, Python, Groovy or Java without any coding knowledge.
19 |
20 | How to enable it
21 |
22 | Go to "Hackvertor settings" in the "Hackvertor" menu and check the following options: Hackvertor -> Settings -> AI -> Use AI to generate code
23 |
24 | How it works
25 |
26 | Go to the Hackvertor menu and create a custom tag (Hackvertor -> Create custom tag). Give it a tag name, such as "Reverse" and select your language. In this example, we will use "JavaScript".
27 | In the code box, type your prompt. For example, "Reverse this text".
28 | Then click the "Use AI to generate code" button. This will generate the JavaScript code for you, based on the given prompt.
29 |
30 | AI custom tags
31 |
32 | Hackvertor also allows you to generate custom tags from AI prompts directly.
33 |
34 | How to enable it
35 |
36 | Go to "Hackvertor settings" in the "Hackvertor" menu and check the following options:
37 |
38 |
39 | - Hackvertor -> Settings -> AI -> Use AI to generate code
40 |
41 |
42 | How it works
43 |
44 | Go to the Hackvertor menu and create a custom tag (Hackvertor -> Create custom tag). Give it a tag name, such as "Reverse" and select "AI" as the language.
45 | In the code box, type your prompt. For example, "Reverse this text".
46 | Then click "Create tag". You can now use this custom tag in the Hackvertor interface.
47 |
48 | Summarise custom tag code
49 |
50 | This will use AI to automatically create a text description of any custom tag. This works when you create or update a custom tag.
51 |
52 | How to enable it
53 |
54 | Go to "Hackvertor settings" in the "Hackvertor" menu and check the following options:
55 |
56 |
57 | - Hackvertor -> Settings -> AI -> Use AI to generate code
58 | - Hackvertor -> Settings -> AI -> Use AI to summarise custom tag code
59 |
60 |
61 | How it works
62 |
63 | Simply create a custom tag in a language, such as JavaScript, and Hackvertor will automatically give you a text description of what the code does.
64 |
65 | Note: This does not work when creating an AI custom tag, as the prompt itself provides a description.
66 |
67 | Learn from Repeater
68 |
69 | This feature analyzes Repeater requests with AI, and tries to automatically produce the relevant encoding and decoding custom tags.
70 |
71 | How to enable it
72 |
73 | Go to "Hackvertor settings" in the "Hackvertor" menu and check the following options:
74 |
75 |
76 | - Hackvertor -> Settings -> AI -> Use AI to generate code
77 | - Hackvertor -> Settings -> AI -> Use AI to learn from Repeater
78 |
79 |
80 | How it works
81 |
82 | In Repeater, you need a request with an unknown encoding. For example, "\x66\x6F\x6F\x62\x61\x72"
83 |
84 | Place this value in a GET parameter. For example:
85 |
86 |
87 | GET /?x=\x66\x6F\x6F\x62\x61\x72 HTTP/2
88 | Host: hackvertor.co.uk
89 | Accept-Encoding: gzip, deflate, br
90 | Accept: */*
91 | Accept-Language: en-US;q=0.9,en;q=0.8
92 | User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.6613.138 Safari/537.36
93 | Cache-Control: max-age=0
94 |
95 |
96 |
97 | Now send this request to Repeater around 5 times. After the 5th request Hackvertor should send the request to the AI for analysis. If successful, it should produce custom tags that encode or decode the encoding.
98 |
99 | If you enable "Debug AI requests" in the Settings, you should see the conversation between Hackvertor and the AI.
100 |
101 | When the tags have been created, they should be available in "Hackvertor -> List custom tags".
102 |
103 |
104 | Copyright © 2015-2025 PortSwigger Ltd.
105 |
106 |
--------------------------------------------------------------------------------
/BappManifest.bmf:
--------------------------------------------------------------------------------
1 | Uuid: 65033cbd2c344fbabe57ac060b5dd100
2 | ExtensionType: 1
3 | Name: Hackvertor
4 | RepoName: hackvertor
5 | ScreenVersion: 2.0.16
6 | SerialVersion: 52
7 | MinPlatformVersion: 4
8 | ProOnly: False
9 | Author: Gareth Heyes, PortSwigger
10 | ShortDescription: Converts data using a tag-based configuration to apply various encoding and escaping operations.
11 | EntryPoint: releases/hackvertor-all.jar
12 | BuildCommand: ./gradlew shadowJar
13 | SupportedProducts: Pro, Community
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # Hackvertor
4 |
5 | Hackvertor is a tag based conversion tool written in Java implemented as a Burp Suite extension. Tags are constructed as follows:
6 | `<@base64>@base64>` the @ symbol is used as an identifier that it's a Hackvertor tag followed by the name of the tag in this case base64.
7 |
8 | Tags also support arguments. The find tag allows you to find a string by regex and has parenthesis after the tag name:
9 | `<@find("\\w")>abc@find>` this indicates it supports arguments. The argument in this case is the regex string to find on the text in-between the tags. Hackvertor allows you to use three types of arguments either strings (double, single), boolean (true, false) or numbers (including hex).
10 |
11 | # Changelog
12 |
13 | **2.0.12 2025-02-13**
14 |
15 | - Added AI features!
16 | - Learn from repeater! Hackvertor will attempt to learn the encoding from repeater requests and generate Python custom tags automatically.
17 | - Summarise custom code tags. When a custom tag is created Hackvertor will use AI to summarise what it does.
18 | - AI custom tags. You can now use prompts in custom tags.
19 | - Use AI to generate code. Hackvertor will generate a custom tag for you if you give it some input/output and instructions
20 |
21 | **2.0.0 2025-01-08**
22 |
23 | - Added tag execution key rehydrate button
24 | - Made HTTP request editor more compact
25 | - Added smart decode feature
26 | - Changed style of tags to `@name>` instead `<@/name>` to help with autocompletion in future
27 |
28 | **1.8.10 2024-01-08**
29 |
30 | - Added new line and space tags
31 | - Added ean13 tag to the tag store
32 | - Allowed regex replace to use capture groups
33 |
34 | **1.8.9 2023-12-22**
35 |
36 | - Fixed #79 No contextual menu entries for requests in Proxy History and Sitemap
37 |
38 | **1.8.8 2023-12-20**
39 |
40 | - Added remove output tag
41 | - Added load from json file
42 | - Added save to json file
43 |
44 | **1.8.6 2023-12-20**
45 |
46 | - Added line numbers to custom tag editor
47 |
48 | **1.8.6 2023-12-19**
49 |
50 | - Added full support for JavaScript in custom tags
51 |
52 | **1.8.5 2023-12-18**
53 |
54 | - Fixed bug where hex default value for custom tag would be quoted
55 |
56 | **1.8.4 2023-11-1**
57 |
58 | - Continued improvements on create tag window.
59 |
60 | **1.8.3 2023-11-1**
61 |
62 | - Disabled install button when tag is installed
63 | - Started work on create new tag to make more room
64 |
65 | **1.8.2 2023-10-31**
66 | - Fixed editing tags without producing duplicates
67 | - Added export to tag store
68 |
69 | **1.8.1 2023-10-30**
70 | - Fixed bug when installing a tag from the tag store with the same name.
71 |
72 | **1.8 2023-10-26**
73 | - Implemented tag store. Installable tags from Github.
74 |
75 | # Installation
76 |
77 | - In order to use Hackvertor you need to open Burp Suite.
78 | - Click the Extender tab
79 | - Click the BApp store tab inside the Extender tab
80 | - Scroll down and click Hackvertor
81 | - Then click install on the right
82 |
83 | # How to use Hackvertor
84 |
85 | To use Hackvertor once it has been installed, click on the Hackvertor tab in the main Burp Suite window. You can then type into the input box to create some text to convert. For instance if you want to convert some text to base64, select the text in the input box then click on the encode tab in Hackvertor, then find the base64 tag and click it. Hackvertor will then add the tag around the selected text and the output window will show a base64 encoded string of your text. It's worth noting that Hackvertor supports an unlimited amount of nesting, you can use multiple tags to encode or decode text. Hackvertor will work from the inner most tag to the outer tag and each step will be converted using the relevant tag you have chosen.
86 |
87 | # Advanced usage
88 |
89 | For more advanced users, you can use tags within repeater tabs. Simply click the repeater tab, right click and select the Hackvertor menu. Then you can use any tag within the repeater tab. Tags will be displayed in the repeater window but when a request is sent they will be converted by Hackvertor and the server will see the converted request. Hackvertor also have a message editor tab, you can select this tab from any request tab in Burp. This will then create the Hackvertor interface inside a request tab, allowing to use the Hackvertor interface to modify a request.
90 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'java'
3 | id "org.javacc.javacc" version "3.0.3"
4 | id 'com.github.johnrengelman.shadow' version '8.1.1'
5 | }
6 |
7 | targetCompatibility = JavaVersion.VERSION_17
8 | sourceCompatibility = JavaVersion.VERSION_17
9 |
10 | compileJava.options.encoding = 'UTF-8'
11 | compileTestJava.options.encoding = 'UTF-8'
12 |
13 | repositories {
14 | mavenCentral()
15 | }
16 |
17 | dependencies {
18 | compileOnly 'net.portswigger.burp.extender:burp-extender-api:1.7.22'
19 | compileOnly 'net.portswigger.burp.extensions:montoya-api:2025.3'
20 | implementation 'commons-codec:commons-codec:1.15'
21 | implementation 'org.apache.commons:commons-lang3:3.12.0'
22 | implementation 'org.unbescape:unbescape:1.1.6.RELEASE'
23 | implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'
24 | implementation 'com.auth0:java-jwt:4.3.0'
25 | implementation 'org.json:json:20230227'
26 | implementation 'commons-io:commons-io:2.14.0'
27 | implementation 'org.apache.commons:commons-compress:1.22'
28 | implementation 'org.brotli:dec:0.1.2'
29 | implementation 'org.python:jython-standalone:2.7.3b1'
30 | implementation 'bsf:bsf:2.4.0'
31 | implementation 'org.apache-extras.beanshell:bsh:2.0b6'
32 | implementation 'org.codehaus.groovy:groovy-all:3.0.16'
33 | implementation 'com.github.javafaker:javafaker:1.0.2'
34 | implementation 'com.fifesoft:autocomplete:3.3.1'
35 | testImplementation 'junit:junit:4.13.2'
36 | testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
37 | implementation 'org.graalvm.js:js-community:24.1.1'
38 | implementation 'org.graalvm.js:js-scriptengine:24.1.1'
39 | }
40 |
41 | sourceSets {
42 | main {
43 | java {
44 | srcDir compileJavacc.outputDirectory
45 | }
46 | }
47 | }
48 |
49 | compileJavacc {
50 | inputDirectory = file('src/main/javacc')
51 | include '**/*.java'
52 | }
53 |
54 | jar {
55 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE
56 | archivesBaseName = project.name + '-all'
57 | from {
58 | configurations.runtimeClasspath.findAll { it.isDirectory() || it.name.endsWith('.jar') }
59 | .collect { it.isDirectory() ? it : zipTree(it) }
60 | }{
61 | exclude "META-INF/*.SF"
62 | exclude "META-INF/*.DSA"
63 | exclude "META-INF/*.RSA"
64 | exclude "META-INF/*.txt"
65 | }
66 | }
67 |
68 | tasks.withType(Jar) {
69 | destinationDirectory = file("$rootDir/releases/")
70 | }
71 |
72 | test {
73 | useJUnitPlatform()
74 | }
75 |
76 | shadowJar {
77 | archiveClassifier.set('')
78 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE
79 | configurations = [project.configurations.runtimeClasspath]
80 | mergeServiceFiles()
81 | }
82 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PortSwigger/hackvertor/48bd4571c825fa3181efb6658830a1da5241b662/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/hackvertor.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'hackvertor'
2 |
--------------------------------------------------------------------------------
/src/main/java/META-INF/MANIFEST.MF:
--------------------------------------------------------------------------------
1 | Manifest-Version: 1.0
2 | Class-Path: ./
3 |
4 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/AES.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import java.io.UnsupportedEncodingException;
4 | import java.security.MessageDigest;
5 | import java.security.NoSuchAlgorithmException;
6 | import java.util.Base64;
7 |
8 | import javax.crypto.Cipher;
9 | import javax.crypto.spec.IvParameterSpec;
10 | import javax.crypto.spec.SecretKeySpec;
11 |
12 | public class AES {
13 |
14 | private static SecretKeySpec secretKey;
15 | private static byte[] key;
16 |
17 | public static void setKey(String myKey) throws NoSuchAlgorithmException, UnsupportedEncodingException, IllegalArgumentException {
18 | MessageDigest sha = null;
19 | key = myKey.getBytes("UTF-8");
20 | if (key.length % 16 != 0) {
21 | throw new IllegalArgumentException("Invalid key length");
22 | }
23 | secretKey = new SecretKeySpec(key, "AES");
24 | }
25 |
26 | public static String encrypt(String strToEncrypt, String secret, String transformations, String iv) throws Exception {
27 | setKey(secret);
28 | IvParameterSpec ivSpec = null;
29 | if (iv.length() > 0) {
30 | ivSpec = new IvParameterSpec(iv.getBytes("UTF-8"));
31 | }
32 | Cipher cipher = Cipher.getInstance(transformations);
33 | if (iv.length() > 0) {
34 | cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
35 | } else {
36 | cipher.init(Cipher.ENCRYPT_MODE, secretKey);
37 | }
38 | return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
39 | }
40 |
41 | public static String decrypt(String strToDecrypt, String secret, String transformations, String iv) throws Exception {
42 | setKey(secret);
43 | IvParameterSpec ivSpec = null;
44 | if (iv.length() > 0) {
45 | ivSpec = new IvParameterSpec(iv.getBytes("UTF-8"));
46 | }
47 | Cipher cipher = Cipher.getInstance(transformations);
48 | if(iv.length() > 0) {
49 | cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
50 | } else {
51 | cipher.init(Cipher.DECRYPT_MODE, secretKey);
52 | }
53 | return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
54 | }
55 | }
--------------------------------------------------------------------------------
/src/main/java/burp/hv/Base58.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import java.math.BigInteger;
4 |
5 | public class Base58 {
6 | private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
7 | private static final BigInteger BASE = BigInteger.valueOf(58);
8 |
9 | public static String encode(byte[] input) {
10 | if (input.length == 0) {
11 | return "";
12 | }
13 |
14 | // Convert the input bytes to a BigInteger
15 | BigInteger num = new BigInteger(1, input);
16 |
17 | // Encode the BigInteger as base58
18 | StringBuilder sb = new StringBuilder();
19 | while (num.compareTo(BigInteger.ZERO) > 0) {
20 | BigInteger[] qr = num.divideAndRemainder(BASE);
21 | sb.append(ALPHABET[qr[1].intValue()]);
22 | num = qr[0];
23 | }
24 |
25 | // Add leading '1' characters for each leading zero byte in the input
26 | for (int i = 0; i < input.length && input[i] == 0; i++) {
27 | sb.append(ALPHABET[0]);
28 | }
29 |
30 | return sb.reverse().toString();
31 | }
32 |
33 | public static byte[] decode(String input) {
34 | if (input.length() == 0) {
35 | return new byte[0];
36 | }
37 |
38 | // Convert the base58 input to a BigInteger
39 | BigInteger num = BigInteger.ZERO;
40 | for (int i = 0; i < input.length(); i++) {
41 | char c = input.charAt(i);
42 | int digit = -1;
43 | for (int j = 0; j < ALPHABET.length; j++) {
44 | if (ALPHABET[j] == c) {
45 | digit = j;
46 | break;
47 | }
48 | }
49 | if (digit == -1) {
50 | throw new IllegalArgumentException("Invalid character '" + c + "' at position " + i);
51 | }
52 | num = num.multiply(BASE).add(BigInteger.valueOf(digit));
53 | }
54 |
55 | // Convert the BigInteger to a byte array
56 | byte[] bytes = num.toByteArray();
57 |
58 | // Remove any leading zero bytes
59 | if (bytes.length > 0 && bytes[0] == 0) {
60 | byte[] tmp = new byte[bytes.length - 1];
61 | System.arraycopy(bytes, 1, tmp, 0, tmp.length);
62 | bytes = tmp;
63 | }
64 |
65 | // Add leading zero bytes for each leading '1' character in the input
66 | int numZeros = 0;
67 | for (int i = 0; i < input.length() && input.charAt(i) == ALPHABET[0]; i++) {
68 | numZeros++;
69 | }
70 | byte[] result = new byte[numZeros + bytes.length];
71 | System.arraycopy(bytes, 0, result, numZeros, bytes.length);
72 |
73 | return result;
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/Burp.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import burp.api.montoya.core.Version;
4 |
5 | public class Burp {
6 | public enum Capability {
7 | REGISTER_HOTKEY(20250300000037651L);
8 |
9 | private final long minimumSupportedBuildNumber;
10 |
11 | Capability(long minimumSupportedBuildNumber) {
12 | this.minimumSupportedBuildNumber = minimumSupportedBuildNumber;
13 | }
14 | }
15 |
16 | private final Version version;
17 |
18 | public Burp(Version version) {
19 | this.version = version;
20 | }
21 |
22 | public boolean hasCapability(Capability capability) {
23 | return version.buildNumber() >= capability.minimumSupportedBuildNumber;
24 | }
25 | }
--------------------------------------------------------------------------------
/src/main/java/burp/hv/HackvertorAction.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import burp.IContextMenuInvocation;
4 | import burp.IResponseInfo;
5 | import burp.hv.ui.ExtensionPanel;
6 | import burp.hv.ui.HackvertorPanel;
7 |
8 | import javax.swing.*;
9 | import java.awt.event.ActionEvent;
10 | import java.nio.charset.StandardCharsets;
11 | import java.util.Arrays;
12 |
13 | public class HackvertorAction extends AbstractAction {
14 |
15 | private final ExtensionPanel extensionPanel;
16 | private final IContextMenuInvocation invocation;
17 | private static final long serialVersionUID = 1L;
18 |
19 | public HackvertorAction(String text, ExtensionPanel extensionPanel, IContextMenuInvocation invocation) {
20 | super(text);
21 | this.extensionPanel = extensionPanel;
22 | this.invocation = invocation;
23 | }
24 |
25 | public void actionPerformed(ActionEvent e) {
26 | byte[] message = null;
27 | switch (invocation.getInvocationContext()) {
28 | case IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST:
29 | case IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST:
30 | case IContextMenuInvocation.CONTEXT_INTRUDER_PAYLOAD_POSITIONS:
31 | message = invocation.getSelectedMessages()[0].getRequest();
32 | break;
33 | case IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE:
34 | message = invocation.getSelectedMessages()[0].getResponse();
35 | break;
36 | }
37 | int[] bounds = invocation.getSelectionBounds();
38 | if (message != null) {
39 | HackvertorPanel hackvertorPanel = extensionPanel.addNewPanel();
40 | if (bounds[0] == bounds[1]) {
41 | IResponseInfo analyzedResponse = HackvertorExtension.helpers.analyzeResponse(message);
42 | byte[] body = Arrays.copyOfRange(message, analyzedResponse.getBodyOffset(), message.length);
43 | if(body.length > 0) {
44 | hackvertorPanel.getInputArea().setText(new String(body, StandardCharsets.ISO_8859_1));
45 | } else {
46 | hackvertorPanel.getInputArea().setText(new String(message, StandardCharsets.ISO_8859_1));
47 | }
48 | } else {
49 | hackvertorPanel.getInputArea().setText("<@auto_decode_no_decrypt>" + new String(Arrays.copyOfRange(message, bounds[0], bounds[1]), StandardCharsets.ISO_8859_1).trim() + "@auto_decode_no_decrypt>");
50 | }
51 | extensionPanel.makeActiveBurpTab();
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/HackvertorExtension.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import burp.*;
4 | import burp.api.montoya.BurpExtension;
5 | import burp.api.montoya.EnhancedCapability;
6 | import burp.api.montoya.MontoyaApi;
7 | import burp.api.montoya.core.Registration;
8 | import burp.api.montoya.http.message.requests.HttpRequest;
9 | import burp.api.montoya.ui.contextmenu.MessageEditorHttpRequestResponse;
10 | import burp.api.montoya.ui.hotkey.HotKeyContext;
11 | import burp.hv.settings.Settings;
12 | import burp.hv.tags.CustomTags;
13 | import burp.hv.tags.Tag;
14 | import burp.hv.ui.ContextMenu;
15 | import burp.hv.ui.ExtensionPanel;
16 | import burp.hv.ui.HackvertorMessageTab;
17 | import burp.hv.ui.MontoyaContextMenu;
18 | import burp.hv.utils.Utils;
19 | import org.bouncycastle.jce.provider.BouncyCastleProvider;
20 |
21 | import javax.swing.*;
22 | import java.awt.*;
23 | import java.io.*;
24 | import java.security.Security;
25 | import java.util.*;
26 | import java.util.List;
27 | import java.util.concurrent.ExecutorService;
28 | import java.util.concurrent.Executors;
29 |
30 | import static burp.hv.Convertors.*;
31 | import static burp.hv.HackvertorExtension.montoyaApi;
32 |
33 | public class HackvertorExtension implements BurpExtension, IBurpExtender, ITab, IExtensionStateListener, IMessageEditorTabFactory {
34 | //TODO Unset on unload
35 | public static String extensionName = "Hackvertor";
36 | public static String version = "v2.0.16";
37 | public static JFrame HackvertorFrame = null;
38 | public static IBurpExtenderCallbacks callbacks;
39 | public static IExtensionHelpers helpers;
40 | public static String tagCodeExecutionKey = null;
41 | public static Ngrams ngrams;
42 | public static PrintWriter stderr;
43 | public static PrintWriter stdout;
44 | public static MontoyaApi montoyaApi;
45 | public static Settings generalSettings;
46 | public static HashMapglobalVariables = new HashMap<>();
47 | public static boolean isNativeTheme;
48 | public static boolean isDarkTheme;
49 | private List NATIVE_LOOK_AND_FEELS = Arrays.asList("GTK","Windows","Aqua","FlatLaf - Burp Light");
50 | public static List DARK_THEMES = Arrays.asList("Darcula","FlatLaf - Burp Dark");
51 |
52 | public static Hackvertor hackvertor;
53 | public static ExtensionPanel extensionPanel;
54 |
55 | public static final ExecutorService executorService = Executors.newSingleThreadExecutor();
56 | public static int requestHistoryPos = 0;
57 | public static boolean hasHotKey = false;
58 | public static ArrayList requestHistory = new ArrayList<>();
59 | public static HashMap tagCount = new HashMap<>();
60 | public static final HashMap> contextTagCount = new HashMap() {
61 | {
62 | put("GET", new HashMap<>());
63 | put("POST", new HashMap<>());
64 | put("JSON", new HashMap<>());
65 | }
66 | };
67 | private JMenuBar burpMenuBar;
68 |
69 | public static int MAX_POPULAR_TAGS = 10;
70 |
71 | @Override
72 | public Set enhancedCapabilities() {
73 | return Set.of(EnhancedCapability.AI_FEATURES);
74 | }
75 |
76 | @Override
77 | public IMessageEditorTab createNewInstance(IMessageEditorController controller, boolean editable) {
78 | return new HackvertorMessageTab(hackvertor);
79 | }
80 |
81 | public static ImageIcon createImageIcon(String path, String description) {
82 | java.net.URL imgURL = HackvertorExtension.class.getResource(path);
83 | if (imgURL != null) {
84 | ImageIcon img = new ImageIcon(imgURL, description);
85 | Image resizedImage = img.getImage().getScaledInstance(80, 80, Image.SCALE_SMOOTH);
86 | return new ImageIcon(resizedImage);
87 | } else {
88 | stderr.println("Couldn't find file: " + path);
89 | return null;
90 | }
91 | }
92 |
93 | public static void print(String s){
94 | System.out.print(s);
95 | callbacks.printOutput(s);
96 | }
97 |
98 | public void registerExtenderCallbacks(final IBurpExtenderCallbacks burpCallbacks) {
99 | generalSettings = new Settings("general", burpCallbacks);
100 | Utils.registerGeneralSettings(generalSettings);
101 | generalSettings.load();
102 | callbacks = burpCallbacks;
103 | helpers = callbacks.getHelpers();
104 | stderr = new PrintWriter(callbacks.getStderr(), true);
105 | stdout = new PrintWriter(callbacks.getStdout(), true);
106 | tagCodeExecutionKey = CustomTags.generateRandomCodeExecutionKey();
107 | callbacks.setExtensionName(extensionName);
108 | Security.addProvider(new BouncyCastleProvider());
109 | SwingUtilities.invokeLater(() -> {
110 | try {
111 | hackvertor = new Hackvertor();
112 | stdout.println(extensionName + " " + version);
113 | CustomTags.loadCustomTags();
114 | Variables.loadGlobalVariables();
115 | registerPayloadProcessors();
116 | extensionPanel = new ExtensionPanel(hackvertor);
117 | callbacks.addSuiteTab(this);
118 | callbacks.registerMessageEditorTabFactory(HackvertorExtension.this);
119 | callbacks.registerContextMenuFactory(new ContextMenu());
120 | callbacks.registerHttpListener(new HttpListener());
121 | callbacks.registerExtensionStateListener(this);
122 | } catch (Exception ignored){
123 |
124 | }
125 | });
126 | //callbacks.printOutput("Look And Feel: "+UIManager.getLookAndFeel().getID());
127 | isNativeTheme = NATIVE_LOOK_AND_FEELS.contains(UIManager.getLookAndFeel().getID());
128 | isDarkTheme = DARK_THEMES.contains(UIManager.getLookAndFeel().getID());
129 | }
130 |
131 | void registerPayloadProcessors() {
132 | ArrayList tags = hackvertor.getTags();
133 | for(int i=0;i {
181 | if (event.messageEditorRequestResponse().isEmpty()) {
182 | return;
183 | }
184 | MessageEditorHttpRequestResponse requestResponse = event.messageEditorRequestResponse().get();
185 | if(requestResponse.selectionOffsets().isPresent() && requestResponse.selectionContext().toString().equalsIgnoreCase("request")) {
186 | String request = requestResponse.requestResponse().request().toString();
187 | int start = requestResponse.selectionOffsets().get().startIndexInclusive();
188 | int end = requestResponse.selectionOffsets().get().endIndexExclusive();
189 | String selectionWithTags = auto_decode_no_decrypt(request.substring(start, end));
190 | String modifiedRequest = request.substring(0, start) + selectionWithTags + request.substring(end);
191 | requestResponse.setRequest(HttpRequest.httpRequest(requestResponse.requestResponse().httpService(), modifiedRequest));
192 | }
193 | });
194 | if(registration.isRegistered()) {
195 | montoyaApi.logging().logToOutput("Successfully registered hotkey handler");
196 | hasHotKey = true;
197 | } else {
198 | montoyaApi.logging().logToError("Failed to register hotkey handler");
199 | }
200 | }
201 | }
202 | }
203 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/HackvertorPayloadProcessor.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import burp.IIntruderPayloadProcessor;
4 | import burp.parser.ParseException;
5 |
6 | import java.util.ArrayList;
7 | import java.util.HashMap;
8 |
9 | import static burp.hv.HackvertorExtension.helpers;
10 |
11 | public class HackvertorPayloadProcessor implements IIntruderPayloadProcessor {
12 | private final Hackvertor hackvertor;
13 | private final String name;
14 | private final String tag;
15 |
16 | HackvertorPayloadProcessor(Hackvertor hackvertor, String name, String tag) {
17 | this.hackvertor = hackvertor;
18 | this.name = name;
19 | this.tag = tag;
20 | }
21 |
22 | public byte[] processPayload(byte[] currentPayload, byte[] originalPayload, byte[] baseValue) {
23 | String input = helpers.bytesToString(currentPayload);
24 | String tagOutput;
25 | try {
26 | tagOutput = Convertors.callTag(new HashMap<>(), hackvertor.getCustomTags(), this.tag, input, new ArrayList(), null);
27 | } catch (ParseException e) {
28 | return null;
29 | }
30 | byte[] output = helpers.stringToBytes(tagOutput);
31 | return output;
32 | }
33 |
34 | public String getProcessorName() {
35 | return this.name;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/HttpListener.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import burp.IBurpExtenderCallbacks;
4 | import burp.IHttpListener;
5 | import burp.IHttpRequestResponse;
6 | import burp.IRequestInfo;
7 | import burp.api.montoya.http.message.responses.analysis.ResponseVariationsAnalyzer;
8 | import burp.hv.ai.AI;
9 | import burp.hv.ai.CodeConversion;
10 | import burp.hv.ai.LearnFromRepeater;
11 | import burp.hv.ai.PromptGenerator;
12 | import burp.hv.settings.InvalidTypeSettingException;
13 | import burp.hv.settings.UnregisteredSettingException;
14 | import burp.hv.tags.CustomTags;
15 | import burp.hv.utils.HttpUtils;
16 | import org.json.JSONArray;
17 | import org.json.JSONObject;
18 |
19 | import java.io.PrintWriter;
20 | import java.io.StringWriter;
21 | import java.util.ArrayList;
22 |
23 | import static burp.hv.HackvertorExtension.montoyaApi;
24 | import static burp.hv.HackvertorExtension.requestHistory;
25 |
26 | public class HttpListener implements IHttpListener {
27 |
28 | public void processHttpMessage(int toolFlag, boolean messageIsRequest, IHttpRequestResponse messageInfo) {
29 | if (!messageIsRequest) {
30 | return;
31 | }
32 |
33 | boolean learnFromRepeater;
34 | boolean debugAi;
35 | boolean allowAiToGenerateCode;
36 | boolean tagsInProxy;
37 | boolean tagsInIntruder;
38 | boolean tagsInRepeater;
39 | boolean tagsInScanner;
40 | boolean tagsInExtensions;
41 | boolean autoUpdateContentLength;
42 | try {
43 | learnFromRepeater = HackvertorExtension.generalSettings.getBoolean("learnFromRepeater");
44 | debugAi = HackvertorExtension.generalSettings.getBoolean("debugAi");
45 | allowAiToGenerateCode = HackvertorExtension.generalSettings.getBoolean("allowAiToGenerateCode");
46 | tagsInProxy = HackvertorExtension.generalSettings.getBoolean("tagsInProxy");
47 | tagsInIntruder = HackvertorExtension.generalSettings.getBoolean("tagsInIntruder");
48 | tagsInRepeater = HackvertorExtension.generalSettings.getBoolean("tagsInRepeater");
49 | tagsInScanner = HackvertorExtension.generalSettings.getBoolean("tagsInScanner");
50 | tagsInExtensions = HackvertorExtension.generalSettings.getBoolean("tagsInExtensions");
51 | autoUpdateContentLength = HackvertorExtension.generalSettings.getBoolean("autoUpdateContentLength");
52 | } catch (UnregisteredSettingException | InvalidTypeSettingException e) {
53 | HackvertorExtension.callbacks.printError("Error loading settings:" + e);
54 | throw new RuntimeException(e);
55 | }
56 |
57 | if(learnFromRepeater && AI.isAiSupported() && toolFlag == IBurpExtenderCallbacks.TOOL_REPEATER) {
58 | HackvertorExtension.requestHistoryPos++;
59 | int maxRequestHistory = 5;
60 | if(HackvertorExtension.requestHistoryPos >= maxRequestHistory) {
61 | HackvertorExtension.requestHistoryPos = 0;
62 | JSONArray headersAndParameters = RequestDiffer.generateHeadersAndParametersJson(requestHistory.toArray(new IRequestInfo[0]));
63 | HackvertorExtension.requestHistory = new ArrayList<>();
64 | LearnFromRepeater.learn(headersAndParameters, allowAiToGenerateCode);
65 | } else {
66 | IRequestInfo currentRequest = HackvertorExtension.helpers.analyzeRequest(messageInfo.getHttpService(), messageInfo.getRequest());
67 | HackvertorExtension.requestHistory.add(currentRequest);
68 | }
69 | }
70 |
71 | switch (toolFlag) {
72 | case IBurpExtenderCallbacks.TOOL_PROXY:
73 | if (!tagsInProxy) {
74 | return;
75 | }
76 | break;
77 | case IBurpExtenderCallbacks.TOOL_INTRUDER:
78 | if (!tagsInIntruder) {
79 | return;
80 | }
81 | break;
82 | case IBurpExtenderCallbacks.TOOL_REPEATER:
83 | if (!tagsInRepeater) {
84 | return;
85 | }
86 | break;
87 | case IBurpExtenderCallbacks.TOOL_SCANNER:
88 | if (!tagsInScanner) {
89 | return;
90 | }
91 | break;
92 | case IBurpExtenderCallbacks.TOOL_EXTENDER:
93 | if (!tagsInExtensions) {
94 | return;
95 | }
96 | break;
97 | default:
98 | return;
99 | }
100 | byte[] request = messageInfo.getRequest();
101 | if (HackvertorExtension.helpers.indexOf(request, HackvertorExtension.helpers.stringToBytes("<@"), false, 0, request.length) > -1) {
102 | String requestStr = HackvertorExtension.helpers.bytesToString(request);
103 | HackvertorExtension.hackvertor.analyzeRequest(HackvertorExtension.helpers.stringToBytes(Hackvertor.removeHackvertorTags(requestStr)), messageInfo);
104 | request = HackvertorExtension.helpers.stringToBytes(HackvertorExtension.hackvertor.convert(requestStr, HackvertorExtension.hackvertor));
105 | if (autoUpdateContentLength) {
106 | request = HttpUtils.fixContentLength(request);
107 | }
108 | messageInfo.setRequest(request);
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/Ngrams.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.InputStreamReader;
7 | import java.net.URL;
8 | import java.util.HashMap;
9 | import java.util.Map;
10 |
11 | public class Ngrams {
12 | private Map ngrams;
13 | private int length;
14 | private double floor;
15 | private String input;
16 |
17 | Ngrams(String filename) throws IOException {
18 | String gramLines = readFile(filename);
19 | String[] lines = gramLines.split("\n");
20 | double n = 0;
21 | String lastKey = "";
22 | Map ngramsSum = new HashMap<>();
23 | Map ngramsLookup = new HashMap<>();
24 | for (int i = 0; i < lines.length; i++) {
25 | String[] line = lines[i].split(" ");
26 | String key = line[0];
27 | lastKey = key;
28 | int count = Integer.parseInt(line[1]);
29 | n += count;
30 | ngramsSum.put(key, count);
31 | }
32 | length = lastKey.length();
33 | for (Map.Entry entry : ngramsSum.entrySet()) {
34 | String key = entry.getKey();
35 | int value = entry.getValue();
36 | ngramsLookup.put(key, Math.log10((value) / n));
37 | }
38 | this.ngrams = ngramsLookup;
39 | floor = Math.log10(0.01 / n);
40 | }
41 |
42 | public Double getScore() {
43 | double score = 0;
44 | String str = input.toUpperCase();
45 | str = str.replaceAll("[^A-Z]", "");
46 | for (int i = 0; i < str.length() - length + 1; i++) {
47 | String text = str.substring(i, i + length);
48 | if (ngrams.containsKey(text)) {
49 | score += ngrams.get(text);
50 | } else {
51 | score += floor;
52 | }
53 | }
54 | return score;
55 | }
56 |
57 | public void setInput(String input) {
58 | this.input = input;
59 | }
60 |
61 | private String readFile(String filename) throws IOException {
62 | URL url = getClass().getResource(filename);
63 | InputStream is = url.openStream();
64 | StringBuilder sb = new StringBuilder();
65 | BufferedReader br = new BufferedReader(new InputStreamReader(is));
66 | String read;
67 |
68 | while ((read = br.readLine()) != null) {
69 | sb.append(read + "\n");
70 | }
71 |
72 | br.close();
73 | return sb.toString();
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/RequestDiffer.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import burp.IParameter;
4 | import burp.IRequestInfo;
5 | import org.json.JSONArray;
6 | import org.json.JSONObject;
7 |
8 | import java.util.*;
9 |
10 | public class RequestDiffer {
11 |
12 | public static JSONArray generateHeadersAndParametersJson(IRequestInfo[] requests) {
13 | JSONArray result = new JSONArray();
14 | if (requests == null || requests.length == 0) return result;
15 |
16 | boolean allIdentical = areAllRequestsIdentical(requests);
17 |
18 | if (allIdentical) {
19 | IRequestInfo lastReq = requests[requests.length - 1];
20 | addAllItems(result, lastReq);
21 | } else {
22 | IRequestInfo previous = null;
23 | for (int i = 0; i < requests.length; i++) {
24 | IRequestInfo current = requests[i];
25 | if (i == 0) {
26 | addAllItems(result, current);
27 | } else {
28 | addDifferences(result, current, previous);
29 | }
30 | previous = current;
31 | }
32 | Set inAll = intersectionOfAllRequests(requests);
33 | result = filterNeverChangedItems(result, inAll);
34 | }
35 |
36 | return result;
37 | }
38 |
39 | private static boolean areAllRequestsIdentical(IRequestInfo[] requests) {
40 | for (int i = 1; i < requests.length; i++) {
41 | if (!requestsEquivalent(requests[0], requests[i])) return false;
42 | }
43 | return true;
44 | }
45 |
46 | private static boolean requestsEquivalent(IRequestInfo a, IRequestInfo b) {
47 | if (!a.getUrl().getPath().equals(b.getUrl().getPath())) return false;
48 | if (!a.getHeaders().equals(b.getHeaders())) return false;
49 | return parametersEqual(a.getParameters(), b.getParameters());
50 | }
51 |
52 | private static boolean parametersEqual(List listA, List listB) {
53 | if (listA.size() != listB.size()) return false;
54 | return parameterSet(listA).equals(parameterSet(listB));
55 | }
56 |
57 | private static Set parameterSet(List params) {
58 | Set set = new HashSet<>();
59 | for (IParameter p : params) {
60 | set.add(p.getType() + "|" + p.getName() + "|" + p.getValue());
61 | }
62 | return set;
63 | }
64 |
65 | private static void addAllItems(JSONArray result, IRequestInfo request) {
66 | for (IParameter param : request.getParameters()) {
67 | String type = parameterTypeText(param.getType());
68 | if (!type.isEmpty()) {
69 | JSONObject obj = new JSONObject();
70 | obj.put("type", type);
71 | obj.put("name", param.getName());
72 | obj.put("value", param.getValue());
73 | result.put(obj);
74 | }
75 | }
76 | String path = request.getUrl().getPath();
77 | if (path.length() > 1) {
78 | JSONObject pathObj = new JSONObject();
79 | pathObj.put("type", "PATH");
80 | pathObj.put("value", path);
81 | result.put(pathObj);
82 | }
83 | List headers = new ArrayList<>(request.getHeaders());
84 | if (!headers.isEmpty()) headers.remove(0);
85 | for (String h : headers) {
86 | JSONObject headerObj = new JSONObject();
87 | headerObj.put("type", "header");
88 | headerObj.put("value", h);
89 | result.put(headerObj);
90 | }
91 | }
92 |
93 | private static void addDifferences(JSONArray result, IRequestInfo current, IRequestInfo previous) {
94 | String curPath = current.getUrl().getPath();
95 | String prevPath = previous.getUrl().getPath();
96 | if (!curPath.equals(prevPath)) {
97 | JSONObject pathObj = new JSONObject();
98 | pathObj.put("type", "PATH");
99 | pathObj.put("value", curPath);
100 | result.put(pathObj);
101 | }
102 | Set curParams = parameterSet(current.getParameters());
103 | Set prevParams = parameterSet(previous.getParameters());
104 | for (String curParam : curParams) {
105 | if (!prevParams.contains(curParam)) {
106 | String[] parts = curParam.split("\\|", 3);
107 | JSONObject obj = new JSONObject();
108 | obj.put("type", parameterTypeText(Integer.parseInt(parts[0])));
109 | obj.put("name", parts[1]);
110 | obj.put("value", parts[2]);
111 | result.put(obj);
112 | }
113 | }
114 | List curHeaders = new ArrayList<>(current.getHeaders());
115 | List prevHeaders = new ArrayList<>(previous.getHeaders());
116 | if (!curHeaders.isEmpty()) curHeaders.remove(0);
117 | if (!prevHeaders.isEmpty()) prevHeaders.remove(0);
118 | Set curHeadersSet = new HashSet<>(curHeaders);
119 | Set prevHeadersSet = new HashSet<>(prevHeaders);
120 | for (String header : curHeadersSet) {
121 | if (!prevHeadersSet.contains(header)) {
122 | JSONObject headerObj = new JSONObject();
123 | headerObj.put("type", "header");
124 | headerObj.put("value", header);
125 | result.put(headerObj);
126 | }
127 | }
128 | }
129 |
130 | private static String parameterTypeText(int type) {
131 | return switch (type) {
132 | case IParameter.PARAM_BODY -> "BODY";
133 | case IParameter.PARAM_COOKIE -> "COOKIE";
134 | case IParameter.PARAM_JSON -> "JSON";
135 | case IParameter.PARAM_URL -> "URL";
136 | default -> "";
137 | };
138 | }
139 |
140 | private static Set buildRequestSet(IRequestInfo req) {
141 | Set set = new HashSet<>();
142 | for (IParameter p : req.getParameters()) {
143 | String t = parameterTypeText(p.getType());
144 | if (!t.isEmpty()) {
145 | set.add(t + "|PARAM|" + p.getName() + "|" + p.getValue());
146 | }
147 | }
148 | String path = req.getUrl().getPath();
149 | if (path.length() > 1) {
150 | set.add("PATH|" + path);
151 | }
152 | List headers = new ArrayList<>(req.getHeaders());
153 | if (!headers.isEmpty()) headers.remove(0);
154 | for (String h : headers) {
155 | set.add("header|" + h);
156 | }
157 | return set;
158 | }
159 |
160 | private static Set intersectionOfAllRequests(IRequestInfo[] requests) {
161 | Set intersection = null;
162 | for (IRequestInfo r : requests) {
163 | Set current = buildRequestSet(r);
164 | if (intersection == null) {
165 | intersection = new HashSet<>(current);
166 | } else {
167 | intersection.retainAll(current);
168 | }
169 | if (intersection.isEmpty()) break;
170 | }
171 | return intersection == null ? new HashSet<>() : intersection;
172 | }
173 |
174 | private static JSONArray filterNeverChangedItems(JSONArray result, Set inAll) {
175 | JSONArray filtered = new JSONArray();
176 | for (int i = 0; i < result.length(); i++) {
177 | JSONObject obj = result.getJSONObject(i);
178 | String type = obj.optString("type");
179 | String name = obj.optString("name", "");
180 | String value = obj.optString("value", "");
181 | String key;
182 | if ("header".equals(type) || "PATH".equals(type)) {
183 | key = type + "|" + value;
184 | } else {
185 | key = type + "|PARAM|" + name + "|" + value;
186 | }
187 | if (!inAll.contains(key)) {
188 | filtered.put(obj);
189 | }
190 | }
191 | return filtered;
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/Variables.java:
--------------------------------------------------------------------------------
1 | package burp.hv;
2 |
3 | import burp.hv.utils.Utils;
4 | import org.json.JSONException;
5 | import org.json.JSONObject;
6 |
7 | import javax.swing.*;
8 | import java.awt.*;
9 | import java.awt.datatransfer.Clipboard;
10 | import java.awt.datatransfer.StringSelection;
11 | import java.awt.event.ActionEvent;
12 | import java.awt.event.ActionListener;
13 | import java.util.Iterator;
14 |
15 | public class Variables {
16 | public static void showGlobalVariablesWindow() {
17 | JPanel createVariablePanel = new JPanel();
18 | JFrame createVariableWindow = Utils.getHackvertorWindowInstance();
19 | createVariableWindow.getContentPane().removeAll();
20 | createVariableWindow.getContentPane().setLayout(new BorderLayout());
21 | createVariableWindow.setTitle("Global variables");
22 | createVariableWindow.setResizable(false);
23 | createVariableWindow.setPreferredSize(new Dimension(500, 200));
24 | createVariableWindow.setVisible(true);
25 | JLabel errorMessage = new JLabel();
26 | errorMessage.setPreferredSize(new Dimension(450, 25));
27 | errorMessage.setForeground(Color.red);
28 |
29 | JLabel variableLabel = new JLabel("Variable name");
30 | variableLabel.setPreferredSize(new Dimension(220, 25));
31 | JTextField variableNameField = new JTextField();
32 | variableNameField.setPreferredSize(new Dimension(220, 30));
33 |
34 | JLabel variableValueLabel = new JLabel("Variable value");
35 | variableValueLabel.setPreferredSize(new Dimension(220, 25));
36 | JTextField variableValueField = new JTextField();
37 | variableValueField.setPreferredSize(new Dimension(220, 30));
38 |
39 | JComboBox variableCombo = new JComboBox();
40 |
41 | JButton closeButton = new JButton("Close");
42 | closeButton.addActionListener(new ActionListener() {
43 | @Override
44 | public void actionPerformed(ActionEvent e) {
45 | createVariableWindow.setVisible(false);
46 | createVariableWindow.getContentPane().removeAll();
47 | }
48 | });
49 | JButton createButton = new JButton("Create/Update variable");
50 | createButton.addActionListener(new ActionListener() {
51 | @Override
52 | public void actionPerformed(ActionEvent e) {
53 | errorMessage.setText("");
54 | String variableName = variableNameField.getText().replaceAll("[^\\w+]", "");
55 | String variableValue = variableValueField.getText();
56 | if (variableName.length() < 1) {
57 | errorMessage.setText("Invalid variable name. Use a-zA-Z_0-9 for variable names");
58 | return;
59 | }
60 | if (variableValue.length() < 1) {
61 | errorMessage.setText("Your variable value cannot be blank");
62 | return;
63 | }
64 | HackvertorExtension.globalVariables.put(variableName, variableValue);
65 | variableCombo.removeAllItems();
66 | for (String variable : HackvertorExtension.globalVariables.keySet()) {
67 | variableCombo.addItem(variable);
68 | }
69 | saveGlobalVariables();
70 | }
71 | });
72 | if (!HackvertorExtension.isNativeTheme && !HackvertorExtension.isDarkTheme) {
73 | createButton.setBackground(Color.decode("#005a70"));
74 | createButton.setForeground(Color.white);
75 | closeButton.setBackground(Color.decode("#005a70"));
76 | closeButton.setForeground(Color.white);
77 | }
78 |
79 | JLabel tagLabel = new JLabel("Variable");
80 | tagLabel.setPreferredSize(new Dimension(50, 25));
81 | variableCombo.setPreferredSize(new Dimension(200, 25));
82 | createVariablePanel.add(tagLabel);
83 | createVariablePanel.add(variableCombo);
84 | for (String variable : HackvertorExtension.globalVariables.keySet()) {
85 | variableCombo.addItem(variable);
86 | }
87 | JButton copyButton = new JButton("copy");
88 | copyButton.addActionListener(new ActionListener() {
89 | @Override
90 | public void actionPerformed(ActionEvent e) {
91 | if (variableCombo.getSelectedIndex() == -1) {
92 | return;
93 | }
94 | String key = variableCombo.getSelectedItem().toString();
95 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
96 | StringSelection variableTag = new StringSelection("<@get_"+key+"/>");
97 | clipboard.setContents(variableTag, null);
98 | }
99 | });
100 | JButton editButton = new JButton("Edit");
101 | editButton.addActionListener(new ActionListener() {
102 | @Override
103 | public void actionPerformed(ActionEvent e) {
104 | if (variableCombo.getSelectedIndex() == -1) {
105 | return;
106 | }
107 | String key = variableCombo.getSelectedItem().toString();
108 | variableNameField.setText(key);
109 | variableValueField.setText(HackvertorExtension.globalVariables.get(key));
110 | }
111 | });
112 | JButton deleteButton = new JButton("Delete");
113 | deleteButton.addActionListener(new ActionListener() {
114 | @Override
115 | public void actionPerformed(ActionEvent e) {
116 | if (variableCombo.getSelectedIndex() == -1) {
117 | return;
118 | }
119 | HackvertorExtension.globalVariables.remove(variableCombo.getSelectedItem().toString());
120 | variableCombo.removeAllItems();
121 | for (String variable : HackvertorExtension.globalVariables.keySet()) {
122 | variableCombo.addItem(variable);
123 | }
124 | saveGlobalVariables();
125 | }
126 | });
127 | createVariablePanel.add(copyButton);
128 | createVariablePanel.add(editButton);
129 | createVariablePanel.add(deleteButton);
130 |
131 | Container pane = createVariableWindow.getContentPane();
132 | createVariablePanel.add(errorMessage);
133 | createVariablePanel.add(variableLabel);
134 | createVariablePanel.add(variableNameField);
135 | createVariablePanel.add(variableValueLabel);
136 | createVariablePanel.add(variableValueField);
137 | createVariablePanel.add(closeButton);
138 | createVariablePanel.add(createButton);
139 | pane.add(createVariablePanel);
140 | createVariableWindow.pack();
141 | createVariableWindow.setLocationRelativeTo(null);
142 | createVariableWindow.setVisible(true);
143 | }
144 |
145 | public static void saveGlobalVariables() {
146 | JSONObject json = new JSONObject(HackvertorExtension.globalVariables);
147 | HackvertorExtension.callbacks.saveExtensionSetting("globalVariables", json.toString());
148 | }
149 |
150 | public static void loadGlobalVariables() {
151 | String json = HackvertorExtension.callbacks.loadExtensionSetting("globalVariables");
152 | if (json != null && json.length() > 0) {
153 | try {
154 | JSONObject jsonObject = new JSONObject(json);
155 | Iterator keys = jsonObject.keys();
156 | while(keys.hasNext()) {
157 | String key = keys.next();
158 | HackvertorExtension.globalVariables.put(key, jsonObject.get(key).toString());
159 | }
160 | } catch (JSONException e) {
161 | HackvertorExtension.alert("Failed to load global variables");
162 | }
163 | }
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ai/AI.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ai;
2 |
3 | import burp.api.montoya.ai.chat.Message;
4 | import burp.api.montoya.ai.chat.PromptOptions;
5 | import burp.api.montoya.ai.chat.PromptResponse;
6 | import burp.hv.utils.Utils;
7 | import jdk.jshell.execution.Util;
8 |
9 | import java.security.MessageDigest;
10 | import java.security.NoSuchAlgorithmException;
11 |
12 | import static burp.hv.HackvertorExtension.*;
13 |
14 | public class AI {
15 | public static LimitedHashMap responseCache = new LimitedHashMap<>(20);
16 |
17 | public static final String featureMessage = "This feature is only available on the AI version of Burp.";
18 | public static long lastExecutionTime = 0;
19 | public static long apiRequestLimitMS = 1000;
20 | private String systemMessage;
21 | private String prompt;
22 | private double temperature;
23 | private boolean bypassRateLimit = false;
24 | public void setBypassRateLimit(boolean bypassRateLimit) {
25 | this.bypassRateLimit = bypassRateLimit;
26 | }
27 |
28 | public void setSystemMessage(String systemMessage) {
29 | this.systemMessage = systemMessage;
30 | }
31 |
32 | public void setPrompt(String prompt) {
33 | this.prompt = prompt;
34 | }
35 |
36 | public void setTemperature(double temperature) {
37 | this.temperature = temperature;
38 | }
39 |
40 | public String getPrompt() {
41 | return this.prompt;
42 | }
43 |
44 | public static boolean isAiSupported() {
45 | return montoyaApi != null && Utils.hasApiMethod(montoyaApi, "ai") && montoyaApi.ai().isEnabled();
46 | }
47 |
48 | public String getSystemMessage() {
49 | return this.systemMessage;
50 | }
51 |
52 | public static String getHash(String input) throws NoSuchAlgorithmException {
53 | MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
54 | messageDigest.update(input.getBytes());
55 | return new String(messageDigest.digest());
56 | }
57 | public String execute() {
58 | try {
59 | if(!isAiSupported()) {
60 | throw new RuntimeException("Montoya AI API is not enabled. You need to enable use AI in the extension tab.");
61 | }
62 | String hash = getHash(this.temperature + this.systemMessage + this.prompt);
63 | if(AI.responseCache.containsKey(hash)) {
64 | return AI.responseCache.get(hash);
65 | }
66 | if(!bypassRateLimit) {
67 | checkLastExecutionTime();
68 | }
69 | PromptResponse response = montoyaApi.ai().prompt().execute(PromptOptions.promptOptions().withTemperature(this.temperature), Message.systemMessage(this.systemMessage), Message.userMessage(this.prompt));
70 | AI.responseCache.put(hash, response.content());
71 | return response.content();
72 | } catch (NoSuchAlgorithmException e) {
73 | throw new RuntimeException(e);
74 | }
75 | }
76 | public void checkLastExecutionTime() {
77 | long now = System.currentTimeMillis();
78 | if(AI.lastExecutionTime > 0) {
79 | long diff = now - AI.lastExecutionTime;
80 | if(diff < AI.apiRequestLimitMS) {
81 | AI.lastExecutionTime = now;
82 | throw new RuntimeException("API request limit hit. Please wait a few seconds.");
83 | }
84 | }
85 | AI.lastExecutionTime = now;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ai/CodeConversion.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ai;
2 |
3 | import burp.hv.settings.InvalidTypeSettingException;
4 | import burp.hv.settings.UnregisteredSettingException;
5 | import org.json.JSONObject;
6 |
7 | import static burp.hv.HackvertorExtension.*;
8 |
9 | public class CodeConversion {
10 |
11 | private CodeConversion(){}
12 | public static String promptToCode(String language, JSONObject aiPrompt, String additionalInstructions) {
13 | AI codeConversionAi = new AI();
14 | codeConversionAi.setBypassRateLimit(true);
15 | codeConversionAi.setTemperature(0.0);
16 | codeConversionAi.setSystemMessage("""
17 | Do not decode anything. Do not output markdown. Only output a""" + " " + language +" function" + """
18 | \nDo not describe anything. Do not explain anything.
19 | You are a""" + " " + language + """
20 | expert. Your code should not contain any comments.
21 | """ + additionalInstructions + """
22 | You should not use any external libraries.
23 | You are going to get an LLM prompt from the user which you should convert to a""" + " " + language + " " + """
24 | function. The function should be called""" + " " + aiPrompt.getString("name") + "." + """
25 | \nUse the following JSON as a list of tests to run to ensure the function works by using the input of the test and ensure it matches the expected value:""" + " " +aiPrompt.getJSONArray("tests") + " " + """
26 | Your code should be clear and concise and you should always return valid""" + " " + language +" function"+ """
27 | . DO NOT EMBED THE TESTS IN THE CODE, USE THE TESTS TO VERIFY THE CODE.
28 | DO NOT INCLUDE TESTS IN THE FINAL CODE ONLY USE THE TESTS TO VERIFY THE FUNCTION WORKS.
29 | DO NOT INCLUDE PRINT STATEMENTS.
30 | REMOVE ALL COMMENTS AND TESTS BEFORE RETURNING THE """ + " " + language.toUpperCase() + " FUNCTION." + """
31 | \nOutput a""" + " " + language + " " + """
32 | function. Do not output markdown.
33 | """);
34 | boolean debugAi;
35 | try {
36 | debugAi = generalSettings.getBoolean("debugAi");
37 | } catch (UnregisteredSettingException | InvalidTypeSettingException e) {
38 | throw new RuntimeException(e);
39 | }
40 | if (debugAi) {
41 | print("--CodeConversion--");
42 | print("Generate code prompt:" + codeConversionAi.getSystemMessage() + "\n" + codeConversionAi.getPrompt());
43 | print("--End CodeConversion--");
44 | }
45 | codeConversionAi.setPrompt(aiPrompt.getString("prompt"));
46 | String response = removeMarkdownMarkers(codeConversionAi.execute(), language);
47 | if (debugAi) {
48 | print("--CodeConversion--");
49 | print("Response:" + response);
50 | print("--End CodeConversion--");
51 | }
52 | return response;
53 | }
54 |
55 | public static String removeMarkdownMarkers(String input, String language) {
56 | return input.trim().replaceAll("^(?i)```" + language, "").replaceAll("```$","").trim();
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ai/ExtractFunctionBody.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ai;
2 |
3 | import burp.hv.settings.InvalidTypeSettingException;
4 | import burp.hv.settings.UnregisteredSettingException;
5 |
6 | import static burp.hv.HackvertorExtension.generalSettings;
7 | import static burp.hv.HackvertorExtension.print;
8 |
9 | public class ExtractFunctionBody {
10 |
11 | private ExtractFunctionBody(){}
12 | public static String extract(String language, String code) {
13 | AI ai = new AI();
14 | ai.setBypassRateLimit(true);
15 | ai.setTemperature(0.0);
16 | ai.setSystemMessage("""
17 | You are a"""+" "+language+" "+"""
18 | expert.
19 | You should take some input from the user as"""+" "+language+" "+"""
20 | code. You should extract the body of the function provided and return it in your response.
21 | If there is no function simply return the code.
22 | Do not describe or explain anything. Do not use Markdown. Output plain text.
23 | """);
24 | boolean debugAi;
25 | try {
26 | debugAi = generalSettings.getBoolean("debugAi");
27 | } catch (UnregisteredSettingException | InvalidTypeSettingException e) {
28 | throw new RuntimeException(e);
29 | }
30 | ai.setPrompt(code);
31 | String response = ai.execute().trim();
32 | if (debugAi) {
33 | print("--ExtractFunctionBody--");
34 | print("Response:" + response);
35 | print("--End ExtractFunctionBody--");
36 | }
37 | return response;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ai/LearnFromRepeater.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ai;
2 |
3 | import burp.hv.HackvertorExtension;
4 | import burp.hv.tags.CustomTags;
5 | import org.json.JSONArray;
6 | import org.json.JSONObject;
7 |
8 | import java.io.PrintWriter;
9 | import java.io.StringWriter;
10 |
11 | public class LearnFromRepeater {
12 | public static void learn(JSONArray headersAndParameters, boolean allowAiToGenerateCode) {
13 | HackvertorExtension.executorService.submit(() -> {
14 | try {
15 | String json = HackvertorExtension.callbacks.loadExtensionSetting("customTags");
16 | JSONArray existingTags;
17 | if (json != null && !json.isEmpty()) {
18 | existingTags = new JSONArray(json);
19 | } else {
20 | existingTags = new JSONArray();
21 | }
22 | JSONArray existingPrompts = new JSONArray();
23 | for (int i = 0; i < existingTags.length(); i++) {
24 | JSONObject existingTag = (JSONObject) existingTags.get(i);
25 | JSONObject obj = new JSONObject();
26 | obj.put("name", existingTag.getString("tagName").replaceFirst("^_", ""));
27 | if (existingTag.has("summary")) {
28 | obj.put("summary", existingTag.getString("summary"));
29 | }
30 | existingPrompts.put(obj);
31 | }
32 | String response = PromptGenerator.generateConversionPrompt(existingPrompts, headersAndParameters);
33 | JSONArray prompts = new JSONArray(response);
34 | CustomTags.loadCustomTags();
35 | for (int i = 0; i < prompts.length(); i++) {
36 | JSONObject aiPrompt = (JSONObject) prompts.get(i);
37 | if (HackvertorExtension.hackvertor.hasCustomTag(aiPrompt.getString("name"))) {
38 | continue;
39 | }
40 |
41 | if (allowAiToGenerateCode) {
42 | String pythonCode = CodeConversion.promptToCode("Python", aiPrompt, "The code generated should run on Python 2.7\n") + "\noutput = " + aiPrompt.getString("name") + "(input)";
43 | if(!CustomTags.createCustomTag(aiPrompt.getString("name"), "Python", pythonCode, "", "", "", "", "", "", 0)) {
44 | HackvertorExtension.stderr.println("Tag with the same name already exists: " + aiPrompt.getString("name"));
45 | }
46 | } else {
47 | if(!CustomTags.createCustomTag(aiPrompt.getString("name"), "AI", aiPrompt.getString("prompt"), "temperature", "String", "1.0", "", "", "", 1)) {
48 | HackvertorExtension.stderr.println("Tag with the same name already exists: " + aiPrompt.getString("name"));
49 | }
50 | }
51 | }
52 | } catch (Throwable throwable) {
53 | StringWriter writer = new StringWriter();
54 | throwable.printStackTrace(new PrintWriter(writer));
55 | HackvertorExtension.montoyaApi.logging().logToError(writer.toString());
56 | } finally {
57 | HackvertorExtension.extensionPanel.refresh();
58 | }
59 | });
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ai/LimitedHashMap.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ai;
2 | import java.util.LinkedHashMap;
3 | import java.util.Map;
4 |
5 | public class LimitedHashMap extends LinkedHashMap {
6 | private final int maxSize;
7 |
8 | public LimitedHashMap(int maxSize) {
9 | this.maxSize = maxSize;
10 | }
11 |
12 | @Override
13 | protected boolean removeEldestEntry(Map.Entry eldest) {
14 | return size() > maxSize;
15 | }
16 | }
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ai/PromptGenerator.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ai;
2 | import burp.IParameter;
3 | import burp.IRequestInfo;
4 | import burp.hv.settings.InvalidTypeSettingException;
5 | import burp.hv.settings.UnregisteredSettingException;
6 | import org.json.JSONArray;
7 | import org.json.JSONObject;
8 |
9 | import java.util.List;
10 |
11 | import static burp.hv.HackvertorExtension.generalSettings;
12 | import static burp.hv.HackvertorExtension.print;
13 |
14 | public class PromptGenerator {
15 | private PromptGenerator(){}
16 |
17 | public static String generateConversionPrompt(JSONArray existingPrompts, JSONArray headersAndParameters) {
18 | boolean debugAi;
19 | try {
20 | debugAi = generalSettings.getBoolean("debugAi");
21 | } catch (UnregisteredSettingException | InvalidTypeSettingException e) {
22 | throw new RuntimeException(e);
23 | }
24 | AI ai = new AI();
25 | String existingPromptsText = "";
26 | if(!existingPrompts.isEmpty()) {
27 | existingPromptsText = "Here are a list of existing prompt names and a summary of what they do, if you find any that do the same thing to these do not add the generated prompt to the JSON array ";
28 | existingPromptsText += existingPrompts;
29 | }
30 | ai.setBypassRateLimit(true);
31 | ai.setSystemMessage("""
32 | You are a web security expert.
33 | Your job is to find potential encodings that can be used for web security.
34 | You should then create two prompts that can be used for an LLM. One that will encode and one that will decode the discovered encoding.
35 | The user will send you a list of headers or parameters for you to analyse in JSON, you should analyse them and look how you can create a prompt that either encodes or decodes the found data.
36 | You should always try to prioritise the most interesting encoding unless it already exists in the existing prompts.
37 | You should not include the data found in this prompt.
38 | If you don't find anything just respond with an empty JSON array.
39 | Return an empty JSON array if the encodings you've found already exist in the list of existing prompts.
40 | Do not decode anything. Do not output markdown. Do not describe what you are doing just return JSON.
41 | You should respond with a JSON array and give your prompt a name that matches this regex "^[a-z0-9_]+$".
42 | The first prompt name should contain encode and the second prompt name should contain decode.
43 | The JSON returned should contain a tests property where a list of tests are added to ensure the prompt works as expected.
44 | You should always use more than one character in the input of the tests to ensure it works correctly.
45 | The JSON structure should be:[{"prompt":"$yourprompt","name":"$promptname","tests":[{"input":"$input","expectedOutput":"$expectedOutput"}]}]
46 | \nEnsure that $yourprompt, $input and $expectedOutput is correctly escaped according to the JSON specification.
47 | Your response should be a valid JSON array that conforms to the JSON specification.
48 | Validate your JSON response and ensure it's valid JSON.
49 | You should correctly escape all strings in the JSON.
50 | """+existingPromptsText+"""
51 | . You should parse the following JSON and then look for encodings in it.
52 | Do not make encodings up. They should exist in the JSON you are analysing.
53 | Here is a list of headers and parameters for you to analyse in JSON:
54 | """);
55 |
56 | ai.setPrompt(headersAndParameters.toString());
57 | ai.setTemperature(0.0);
58 | if(debugAi) {
59 | print("--PromptGenerator--");
60 | print(ai.getSystemMessage() + "\n\n");
61 | print(ai.getPrompt() + "\n\n");
62 | print("--end PromptGenerator--");
63 | }
64 | String response = ai.execute().trim();
65 | if(debugAi) {
66 | print("--PromptGenerator--");
67 | print(response + "\n\n");
68 | print("--end PromptGenerator--");
69 | }
70 | return response;
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ai/RemoveCodeReferences.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ai;
2 |
3 | import burp.hv.settings.InvalidTypeSettingException;
4 | import burp.hv.settings.UnregisteredSettingException;
5 |
6 | import static burp.hv.HackvertorExtension.generalSettings;
7 | import static burp.hv.HackvertorExtension.print;
8 |
9 | public class RemoveCodeReferences {
10 |
11 | private RemoveCodeReferences(){}
12 | public static String remove(String language, String code) {
13 | AI ai = new AI();
14 | ai.setBypassRateLimit(true);
15 | ai.setTemperature(0.0);
16 | ai.setSystemMessage("""
17 | You are a"""+" "+language+" "+"""
18 | expert. Your job is to go through the"""+" "+language+" "+"""
19 | code supplied by the user and remove every variable, function argument and reference and return the final result.
20 | Do not describe or explain anything. Do not use Markdown. Output plain text
21 | """);
22 | boolean debugAi;
23 | try {
24 | debugAi = generalSettings.getBoolean("debugAi");
25 | } catch (UnregisteredSettingException | InvalidTypeSettingException e) {
26 | throw new RuntimeException(e);
27 | }
28 | ai.setPrompt(code);
29 | String response = ai.execute().trim();
30 | if (debugAi) {
31 | print("--RemoveCodeReferences--");
32 | print("Response:" + response);
33 | print("--End RemoveCodeReferences--");
34 | }
35 | return response;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ai/SummariseCode.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ai;
2 |
3 | import burp.hv.settings.InvalidTypeSettingException;
4 | import burp.hv.settings.UnregisteredSettingException;
5 |
6 | import static burp.hv.HackvertorExtension.generalSettings;
7 | import static burp.hv.HackvertorExtension.print;
8 |
9 | public class SummariseCode {
10 |
11 | private SummariseCode(){}
12 | public static String getSummary(String language, String code) {
13 | AI ai = new AI();
14 | ai.setBypassRateLimit(true);
15 | ai.setTemperature(0.0);
16 | ai.setSystemMessage("""
17 | You are a"""+" "+language+" "+"""
18 | expert. The user has already defined the variable "input".
19 | You should take some input from the user as"""+" "+language+" "+"""
20 | code. You should concisely summarise what the code does in one or two sentences and return it in your response.
21 | In addition you should provide a test input and expected output in your summary.
22 | Do not describe or explain anything. Do not use Markdown. Output plain text.
23 | """);
24 | boolean debugAi;
25 | try {
26 | debugAi = generalSettings.getBoolean("debugAi");
27 | } catch (UnregisteredSettingException | InvalidTypeSettingException e) {
28 | throw new RuntimeException(e);
29 | }
30 | ai.setPrompt(code);
31 | String response = ai.execute().trim();
32 | if (debugAi) {
33 | print("--SummariseCode--");
34 | print("Response:" + response);
35 | print("--End SummariseCode--");
36 | }
37 | return response;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/settings/InvalidTypeSettingException.java:
--------------------------------------------------------------------------------
1 | package burp.hv.settings;
2 |
3 | public class InvalidTypeSettingException extends Exception {
4 | public InvalidTypeSettingException() {
5 | super("Incorrect type");
6 | }
7 |
8 | public InvalidTypeSettingException(String message) {
9 | super(message);
10 | }
11 |
12 | public InvalidTypeSettingException(String message, Throwable cause) {
13 | super(message, cause);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/settings/UnregisteredSettingException.java:
--------------------------------------------------------------------------------
1 | package burp.hv.settings;
2 |
3 | public class UnregisteredSettingException extends Exception {
4 | public UnregisteredSettingException() {
5 | super("Setting not registered");
6 | }
7 |
8 | public UnregisteredSettingException(String message) {
9 | super(message);
10 | }
11 |
12 | public UnregisteredSettingException(String message, Throwable cause) {
13 | super(message, cause);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/tags/Tag.java:
--------------------------------------------------------------------------------
1 | package burp.hv.tags;
2 |
3 | public class Tag {
4 | public enum Category {
5 | Custom, Globals, Variables, Encode, Decode, String, Convert, Conditions, Math, Hash, HMAC, Fake, Charsets, Compression, Date, Encrypt, Decrypt, Languages, System, XSS
6 | };
7 |
8 | public Category category;
9 | public String name;
10 | public boolean hasInput = true;
11 | public String tooltip;
12 | public TagArgument argument1 = null;
13 | public TagArgument argument2 = null;
14 | public TagArgument argument3 = null;
15 | public TagArgument argument4 = null;
16 |
17 | public Tag(Category tagCategory, String tagName, boolean hasInput, String tooltip) {
18 | this.category = tagCategory;
19 | this.name = tagName;
20 | this.hasInput = hasInput;
21 | this.tooltip = tooltip;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/tags/TagArgument.java:
--------------------------------------------------------------------------------
1 | package burp.hv.tags;
2 |
3 | public class TagArgument {
4 | public String type;
5 | public String value;
6 |
7 | public TagArgument(String type, String value) {
8 | this.type = type;
9 | this.value = value;
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/tags/TagStore.java:
--------------------------------------------------------------------------------
1 | package burp.hv.tags;
2 |
3 | import burp.hv.HackvertorExtension;
4 | import burp.hv.ui.HackvertorInput;
5 | import burp.hv.utils.TagUtils;
6 | import burp.hv.utils.Utils;
7 | import org.json.JSONArray;
8 | import org.json.JSONException;
9 | import org.json.JSONObject;
10 |
11 | import javax.swing.*;
12 | import javax.swing.table.DefaultTableModel;
13 | import javax.swing.text.JTextComponent;
14 | import java.awt.*;
15 | import java.io.BufferedReader;
16 | import java.io.InputStream;
17 | import java.io.InputStreamReader;
18 | import java.net.HttpURLConnection;
19 | import java.net.URI;
20 | import java.net.URL;
21 | import java.util.HashMap;
22 |
23 | public class TagStore {
24 | public static void showTagStore() {
25 | final String TAG_STORE_URL = "https://raw.githubusercontent.com/hackvertor/hackvertor/master/tag-store/";
26 | //final String TAG_STORE_URL = "http://127.0.0.1:4000/";
27 | String jsonResponse = makeHttpRequest(TAG_STORE_URL + "tag-store.json", "GET");
28 | if(jsonResponse == null) {
29 | HackvertorExtension.callbacks.printError("Unable to load tag store JSON");
30 | HackvertorExtension.alert("Unable to load the tag store. Store may be down.");
31 | return;
32 | }
33 | JSONArray tagStore;
34 | try {
35 | tagStore = new JSONArray(jsonResponse);
36 | } catch (JSONException ex) {
37 | HackvertorExtension.alert("Unable to load the tag store. Store may be down.");
38 | HackvertorExtension.callbacks.printError("Invalid JSON");
39 | return;
40 | }
41 |
42 | if(tagStore.isEmpty()) {
43 | HackvertorExtension.alert("Unable to load the tag store. Tag store JSON not found.");
44 | HackvertorExtension.callbacks.printError("Unable to retrieve JSON");
45 | return;
46 | }
47 | HashMap storeCode = new HashMap<>();
48 | JFrame tagStoreWindow = Utils.getHackvertorWindowInstance();
49 | tagStoreWindow.getContentPane().removeAll();
50 | tagStoreWindow.getContentPane().setLayout(new BorderLayout());
51 | tagStoreWindow.setTitle("Hackvertor tag store");
52 | JPanel optionsPanel = new JPanel(new BorderLayout());
53 | Utils.setMarginAndPadding(optionsPanel, 10);
54 | optionsPanel.setVisible(false);
55 | JLabel title = new JLabel("Title here");
56 | Utils.setMarginAndPadding(title, 10);
57 | title.setFont(new Font("Arial",Font.BOLD,30));
58 | title.putClientProperty("html.disable", Boolean.TRUE);
59 | JPanel buttonsPanel = new JPanel(new BorderLayout());
60 | Utils.setMarginAndPadding(buttonsPanel, 10);
61 | JButton installButton = new JButton("Install tag");
62 | JButton closeButton = new JButton("Close");
63 | buttonsPanel.add(closeButton, BorderLayout.WEST);
64 | buttonsPanel.add(installButton, BorderLayout.EAST);
65 | closeButton.addActionListener(e -> {
66 | tagStoreWindow.setVisible(false);
67 | tagStoreWindow.getContentPane().removeAll();
68 | });
69 | Utils.setMarginAndPadding(closeButton, 10);
70 | Utils.setMarginAndPadding(installButton, 10);
71 | optionsPanel.add(title, BorderLayout.NORTH);
72 | JTextComponent.removeKeymap("RTextAreaKeymap");
73 | HackvertorInput codeArea = new HackvertorInput();
74 | codeArea.setEditable(false);
75 | codeArea.setText("Code goes here");
76 | JScrollPane codeScroller = new JScrollPane(codeArea);
77 | Utils.setMarginAndPadding(codeScroller, 10);
78 | JTextArea description = new JTextArea("Description goes here");
79 | description.setEditable(false);
80 | description.putClientProperty("html.disable", Boolean.TRUE);
81 | JScrollPane descScroller = new JScrollPane(description, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
82 | Utils.setMarginAndPadding(descScroller, 10);
83 | JPanel centerPanel = new JPanel(new BorderLayout());
84 | centerPanel.add(descScroller, BorderLayout.NORTH);
85 | centerPanel.add(codeScroller, BorderLayout.CENTER);
86 | optionsPanel.add(centerPanel, BorderLayout.CENTER);
87 | optionsPanel.add(buttonsPanel, BorderLayout.SOUTH);
88 | JPanel tagStorePanel = new JPanel(new BorderLayout());
89 | String[] columnNames = {"Tag name", "Author", "Language"};
90 | DefaultTableModel tagStoreModel = new DefaultTableModel(columnNames, 0);
91 | HashMap storeTags = new HashMap();
92 | for(int i=0;i {
106 | HackvertorExtension.alert("Custom tags can compromise your system. Please ensure you've evaluated the code before you install it.");
107 | int confirm = JOptionPane.showConfirmDialog(null, "Are you sure you want to install this custom tag?");
108 | if(confirm == 0) {
109 | int selectedRow = storeTable.getSelectedRow();
110 | String tagName = (String) storeTable.getValueAt(selectedRow, 0);
111 | if (!TagUtils.validateTagName(tagName)) {
112 | HackvertorExtension.alert("Invalid tag name. Use a-zA-Z_0-9 for tag names");
113 | return;
114 | }
115 | String code = storeCode.get(tagName);
116 | JSONObject tag = storeTags.get(tagName);
117 | int numberOfArgs = tag.getInt("numberOfArgs");
118 | String language = tag.getString("language");
119 | if (!TagUtils.validateCodeLength(code)) {
120 | HackvertorExtension.alert("Invalid code unable to install tag. Code cannot be blank");
121 | return;
122 | }
123 | String argument1 = null;
124 | String argument1Type = null;
125 | String argument1Default = null;
126 | String argument2 = null;
127 | String argument2Type = null;
128 | String argument2Default = null;
129 | if(numberOfArgs > 0) {
130 | argument1 = tag.getString("argument1");
131 | argument1Type = tag.getString("argument1Type");
132 | argument1Default = tag.getString("argument1Default");
133 | argument2 = tag.getString("argument2");
134 | argument2Type = tag.getString("argument2Type");
135 | argument2Default = tag.getString("argument2Default");
136 | if (!TagUtils.validateParam(argument1)) {
137 | HackvertorExtension.alert("Invalid param name. For argument1. Use " + TagUtils.paramRegex);
138 | return;
139 | }
140 | if (argument1Type.equals("Number") && !TagUtils.validateTagParamNumber(argument1Default)) {
141 | HackvertorExtension.alert("Invalid default value for argument1. Use " + TagUtils.numberRegex);
142 | return;
143 | }
144 | if (!TagUtils.validateParam(argument2)) {
145 | HackvertorExtension.alert("Invalid param name for argument2. Use " + TagUtils.paramRegex);
146 | return;
147 | }
148 | if (argument2Type.equals("Number") && !TagUtils.validateTagParamNumber(argument2Default)) {
149 | HackvertorExtension.alert("Invalid default value for argument2. Use " + TagUtils.numberRegex);
150 | return;
151 | }
152 | }
153 | CustomTags.loadCustomTags();
154 | if(HackvertorExtension.hackvertor.hasCustomTag(tagName)) {
155 | CustomTags.updateCustomTag( "_" + tagName, language, code, argument1, argument1Type, argument1Default, argument2, argument2Type, argument2Default, numberOfArgs);
156 | } else {
157 | CustomTags.createCustomTag(tagName, language, code, argument1, argument1Type, argument1Default, argument2, argument2Type, argument2Default, numberOfArgs);
158 | }
159 | CustomTags.loadCustomTags();
160 | HackvertorExtension.alert("Successfully installed the tag");
161 | }
162 | });
163 | selectionModel.addListSelectionListener(e -> {
164 | if(e.getValueIsAdjusting()) {
165 | return;
166 | }
167 | int selectedRow = storeTable.getSelectedRow();
168 | String tagName = (String) storeTable.getValueAt(selectedRow, 0);
169 | tagName = TagUtils.sanitizeTagName(tagName);
170 | String code = null;
171 | CustomTags.loadCustomTags();
172 | if(HackvertorExtension.hackvertor.hasCustomTag(tagName)) {
173 | installButton.setEnabled(false);
174 | } else {
175 | installButton.setEnabled(true);
176 | }
177 |
178 | if(storeCode.containsKey(tagName)) {
179 | code = storeCode.get(tagName);
180 | } else {
181 | code = makeHttpRequest(TAG_STORE_URL+tagName+"/"+tagName+ TagUtils.getExtensionFromLanguage(storeTags.get(tagName).getString("language")), "GET");
182 | if(code == null) {
183 | HackvertorExtension.callbacks.printError("Unable get retrieve code for tag:"+tagName);
184 | return;
185 | }
186 | storeCode.put(tagName, code);
187 | }
188 | title.setText(tagName);
189 | description.setText(storeTags.get(tagName).getString("description"));
190 | codeArea.setTabSize(3);
191 | codeArea.setText(code);
192 | codeArea.setCaretPosition(0);
193 | optionsPanel.setVisible(true);
194 | });
195 | JScrollPane scrollPane = new JScrollPane(storeTable);
196 | tagStorePanel.add(scrollPane, BorderLayout.WEST);
197 | tagStorePanel.add(optionsPanel, BorderLayout.CENTER);
198 | tagStoreWindow.add(tagStorePanel);
199 | tagStoreWindow.setResizable(true);
200 | tagStoreWindow.setPreferredSize(new Dimension(1000, 700));
201 | tagStoreWindow.pack();
202 | tagStoreWindow.setLocationRelativeTo(null);
203 | tagStoreWindow.setVisible(true);
204 | }
205 |
206 | public static String makeHttpRequest(String requestUrl, String method) {
207 | HttpURLConnection connection = null;
208 |
209 | try {
210 | URL url = new URI(requestUrl).toURL();
211 | connection = (HttpURLConnection) url.openConnection();
212 | connection.setRequestProperty("Content-Type", "application/json");
213 | connection.setUseCaches(false);
214 | connection.setDoOutput(true);
215 | connection.setRequestMethod(method);
216 | InputStream is = connection.getInputStream();
217 | BufferedReader rd = new BufferedReader(new InputStreamReader(is));
218 | StringBuilder response = new StringBuilder();
219 | String line;
220 | while ((line = rd.readLine()) != null) {
221 | response.append(line);
222 | response.append(System.lineSeparator());
223 | }
224 | rd.close();
225 | return response.toString();
226 | } catch (Exception e) {
227 | HackvertorExtension.callbacks.printError("Error making HTTP request:" + e);
228 | e.printStackTrace();
229 | return null;
230 | } finally {
231 | if (connection != null) {
232 | connection.disconnect();
233 | }
234 | }
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ui/ContextMenu.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ui;
2 |
3 | import burp.*;
4 | import burp.hv.*;
5 | import burp.hv.ai.AI;
6 | import burp.hv.ai.LearnFromRepeater;
7 | import burp.hv.settings.InvalidTypeSettingException;
8 | import burp.hv.settings.UnregisteredSettingException;
9 | import burp.hv.tags.CustomTags;
10 | import burp.hv.tags.Tag;
11 | import burp.hv.utils.TagUtils;
12 | import burp.hv.utils.UrlUtils;
13 | import burp.hv.utils.Utils;
14 | import org.json.JSONArray;
15 |
16 | import javax.swing.*;
17 | import java.awt.*;
18 | import java.awt.datatransfer.Clipboard;
19 | import java.awt.datatransfer.StringSelection;
20 | import java.io.ByteArrayOutputStream;
21 | import java.io.IOException;
22 | import java.net.URL;
23 | import java.util.List;
24 | import java.util.*;
25 | import java.util.concurrent.atomic.AtomicBoolean;
26 |
27 | import static burp.hv.Convertors.auto_decode_no_decrypt;
28 | import static burp.hv.HackvertorExtension.*;
29 |
30 | public class ContextMenu implements IContextMenuFactory {
31 | public List createMenuItems(IContextMenuInvocation invocation) {
32 | int[] bounds = invocation.getSelectionBounds();
33 | switch (invocation.getInvocationContext()) {
34 | case IContextMenuInvocation.CONTEXT_INTRUDER_PAYLOAD_POSITIONS:
35 | case IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST:
36 | case IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST:
37 | case IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE:
38 | case IContextMenuInvocation.CONTEXT_TARGET_SITE_MAP_TREE:
39 | case IContextMenuInvocation.CONTEXT_TARGET_SITE_MAP_TABLE:
40 | case IContextMenuInvocation.CONTEXT_PROXY_HISTORY:
41 | break;
42 | default:
43 | return null;
44 | }
45 | boolean allowTagCount;
46 | boolean sortTagCategories;
47 | boolean learnFromRepeater;
48 | boolean allowAiToGenerateCode;
49 | try {
50 | learnFromRepeater = HackvertorExtension.generalSettings.getBoolean("learnFromRepeater");
51 | allowAiToGenerateCode = HackvertorExtension.generalSettings.getBoolean("allowAiToGenerateCode");
52 | allowTagCount = HackvertorExtension.generalSettings.getBoolean("allowTagCount");
53 | sortTagCategories = HackvertorExtension.generalSettings.getBoolean("sortTagCategories");
54 | } catch (UnregisteredSettingException | InvalidTypeSettingException e) {
55 | HackvertorExtension.callbacks.printError("Error loading settings:" + e);
56 | throw new RuntimeException(e);
57 | }
58 |
59 | List menu = new ArrayList();
60 | JMenu submenu = new JMenu("Hackvertor");
61 | Action hackvertorAction;
62 | if (invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE && bounds != null && bounds[0] == bounds[1]) {
63 | hackvertorAction = new HackvertorAction("Send response body to Hackvertor", HackvertorExtension.extensionPanel, invocation);
64 | } else {
65 | hackvertorAction = new HackvertorAction("Send to Hackvertor", HackvertorExtension.extensionPanel, invocation);
66 | }
67 | JMenuItem sendToHackvertor = new JMenuItem(hackvertorAction);
68 | submenu.add(sendToHackvertor);
69 |
70 | switch(invocation.getInvocationContext()) {
71 | case IContextMenuInvocation.CONTEXT_TARGET_SITE_MAP_TREE:
72 | case IContextMenuInvocation.CONTEXT_TARGET_SITE_MAP_TABLE:
73 | case IContextMenuInvocation.CONTEXT_PROXY_HISTORY:
74 | case IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST:
75 | case IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_RESPONSE:
76 | menu.add(submenu);
77 | return menu;
78 | }
79 |
80 | JMenuItem copyUrl = new JMenuItem("Copy URL");
81 | copyUrl.addActionListener(e -> {
82 | String converted = HackvertorExtension.hackvertor.convert(HackvertorExtension.helpers.bytesToString(invocation.getSelectedMessages()[0].getRequest()), null);
83 | URL url = HackvertorExtension.helpers.analyzeRequest(invocation.getSelectedMessages()[0].getHttpService(), HackvertorExtension.helpers.stringToBytes(converted)).getUrl();
84 | StringSelection stringSelection = null;
85 | stringSelection = new StringSelection(UrlUtils.buildUrl(url));
86 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
87 | clipboard.setContents(stringSelection, null);
88 | });
89 | submenu.add(copyUrl);
90 |
91 | JMenuItem convert = new JMenuItem("Convert tags");
92 | convert.addActionListener(e -> {
93 | if (invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST || invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST) {
94 | byte[] message = invocation.getSelectedMessages()[0].getRequest();
95 | invocation.getSelectedMessages()[0].setRequest(HackvertorExtension.helpers.stringToBytes(HackvertorExtension.hackvertor.convert(HackvertorExtension.helpers.bytesToString(message), null)));
96 | }
97 | });
98 | submenu.add(convert);
99 | JMenuItem learnFromThisRequest = new JMenuItem("Learn encoding from this request");
100 | learnFromThisRequest.setEnabled(learnFromRepeater && AI.isAiSupported());
101 | learnFromThisRequest.addActionListener(e -> {
102 | if (invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST || invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST) {
103 | IHttpRequestResponse messageInfo = invocation.getSelectedMessages()[0];
104 | if(messageInfo.getHttpService() != null && messageInfo.getRequest() != null) {
105 | IRequestInfo currentRequest = HackvertorExtension.helpers.analyzeRequest(messageInfo.getHttpService(), messageInfo.getRequest());
106 | JSONArray headersAndParameters = RequestDiffer.generateHeadersAndParametersJson(new IRequestInfo[]{currentRequest});
107 | LearnFromRepeater.learn(headersAndParameters, allowAiToGenerateCode);
108 | }
109 | }
110 | });
111 | submenu.add(learnFromThisRequest);
112 | JMenuItem autodecodeConvert;
113 | Burp burp = new Burp(montoyaApi.burpSuite().version());
114 | if(hasHotKey) {
115 | autodecodeConvert = new JMenuItem("Smart decode (CTRL+Alt+D)");
116 | } else {
117 | autodecodeConvert = new JMenuItem("Smart decode");
118 | }
119 | autodecodeConvert.setEnabled(bounds != null && bounds[0] != bounds[1]);
120 | autodecodeConvert.addActionListener(e -> {
121 | if (invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST || invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST) {
122 | byte[] message = invocation.getSelectedMessages()[0].getRequest();
123 | byte[] selection = Arrays.copyOfRange(message, bounds[0], bounds[1]);
124 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
125 | try {
126 | byte[] convertedSelection = HackvertorExtension.helpers.stringToBytes(auto_decode_no_decrypt(HackvertorExtension.helpers.bytesToString(selection)));
127 | outputStream.write(Arrays.copyOfRange(message, 0, bounds[0]));
128 | outputStream.write(convertedSelection);
129 | outputStream.write(Arrays.copyOfRange(message, bounds[1], message.length));
130 | outputStream.flush();
131 | invocation.getSelectedMessages()[0].setRequest(outputStream.toByteArray());
132 | Object source = invocation.getInputEvent().getSource();
133 | if (source instanceof JTextArea) {
134 | ((JTextArea) source).select(bounds[0], bounds[0] + convertedSelection.length);
135 | }
136 | } catch (IOException e1) {
137 | System.err.println(e1.toString());
138 | }
139 | }
140 | });
141 | submenu.add(autodecodeConvert);
142 | submenu.addSeparator();
143 | CustomTags.loadCustomTags();
144 | if(allowTagCount) {
145 | JMenu contextPopularTags = new JMenu("Popular tags for this context");
146 | ArrayList tags = HackvertorExtension.hackvertor.getTags();
147 | byte[] message = invocation.getSelectedMessages()[0].getRequest();
148 | IRequestInfo analyzedRequest = HackvertorExtension.helpers.analyzeRequest(message);
149 | String context = Utils.getContext(analyzedRequest);
150 | if(HackvertorExtension.contextTagCount.containsKey(context)) {
151 | if(HackvertorExtension.contextTagCount.get(context) != null) {
152 | AtomicBoolean foundTags = new AtomicBoolean(false);
153 | HackvertorExtension.contextTagCount.get(context).entrySet().stream().limit(HackvertorExtension.MAX_POPULAR_TAGS)
154 | .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
155 | .forEach(entry -> {
156 | JMenuItem tagMenuItem = new JMenuItem(entry.getKey() + "(" + entry.getValue() + ")");
157 | Tag tagObj = TagUtils.getTagByTagName(tags, entry.getKey());
158 | tagMenuItem.addActionListener(TagUtils.generateTagActionListener(invocation, tagObj));
159 | contextPopularTags.add(tagMenuItem);
160 | foundTags.set(true);
161 | });
162 | if(foundTags.get()) {
163 | submenu.add(contextPopularTags);
164 | }
165 | }
166 | }
167 | JMenu popularTags = new JMenu("Popular tags");
168 | HackvertorExtension.tagCount.entrySet().stream().limit(HackvertorExtension.MAX_POPULAR_TAGS)
169 | .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))
170 | .forEach(entry -> {
171 | JMenuItem tagMenuItem = new JMenuItem(entry.getKey() + "("+entry.getValue()+")");
172 | Tag tagObj = TagUtils.getTagByTagName(tags, entry.getKey());
173 | tagMenuItem.addActionListener(TagUtils.generateTagActionListener(invocation, tagObj));
174 | popularTags.add(tagMenuItem);
175 | });
176 | if(!HackvertorExtension.tagCount.isEmpty()) {
177 | submenu.add(popularTags);
178 | }
179 | }
180 | List categories = new ArrayList<>();
181 | for (Tag.Category category : Tag.Category.values()) {
182 | categories.add(category.name());
183 | }
184 | if(sortTagCategories) {
185 | Collections.sort(categories);
186 | }
187 | for (String category : categories) {
188 | JMenu categoryMenu = TagUtils.createTagMenuForCategory(HackvertorExtension.hackvertor.getTags(), Tag.Category.valueOf(category), invocation, "", false, null);
189 | submenu.add(categoryMenu);
190 | }
191 | menu.add(submenu);
192 | return menu;
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ui/ExtensionPanel.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ui;
2 |
3 | import burp.hv.*;
4 | import burp.hv.settings.InvalidTypeSettingException;
5 | import burp.hv.settings.UnregisteredSettingException;
6 | import burp.hv.tags.Tag;
7 | import burp.hv.utils.TagUtils;
8 |
9 | import javax.swing.*;
10 | import javax.swing.event.ChangeEvent;
11 | import javax.swing.event.ChangeListener;
12 | import java.awt.event.ComponentAdapter;
13 | import java.awt.event.ComponentEvent;
14 |
15 | public class ExtensionPanel extends JTabbedPaneClosable {
16 | private int tabCounter = 1;
17 |
18 | private final Hackvertor hackvertor;
19 |
20 | public ExtensionPanel(Hackvertor hackvertor){
21 | this.hackvertor = hackvertor;
22 | this.addComponentListener(new ComponentAdapter() {
23 | @Override
24 | public void componentShown(ComponentEvent e) {
25 | HackvertorPanel selectedPanel = (HackvertorPanel) getComponentAt(ExtensionPanel.this.getSelectedIndex());
26 | selectedPanel.getInputArea().requestFocusInWindow();
27 | boolean allowAutoConvertClipboard;
28 | if(HackvertorExtension.generalSettings == null) {
29 | return;
30 | }
31 | try {
32 | allowAutoConvertClipboard = HackvertorExtension.generalSettings.getBoolean("allowAutoConvertClipboard");
33 | } catch (UnregisteredSettingException | InvalidTypeSettingException ex) {
34 | HackvertorExtension.callbacks.printError("Error loading settings:" + ex);
35 | throw new RuntimeException(ex);
36 | }
37 | if (ExtensionPanel.this.getSelectedIndex() == -1) {
38 | return;
39 | }
40 | if(allowAutoConvertClipboard) {
41 | selectedPanel.readClipboardAndDecode();
42 | }
43 | }
44 | });
45 |
46 | //TODO Move to HackvertorPanel class
47 | this.addTab("1", new HackvertorPanel(hackvertor, true, false));
48 | this.addTab("...", new JPanel());
49 | this.addChangeListener(new ChangeListener() {
50 | public void stateChanged(ChangeEvent e) {
51 | if (ExtensionPanel.this.getSelectedIndex() == -1) {
52 | return;
53 | }
54 | if (ExtensionPanel.this.clickedDelete) {
55 | ExtensionPanel.this.clickedDelete = false;
56 | if (ExtensionPanel.this.getTabCount() > 1) {
57 | if (ExtensionPanel.this.getSelectedIndex() == ExtensionPanel.this.getTabCount() - 1) {
58 | ExtensionPanel.this.setSelectedIndex(ExtensionPanel.this.getTabCount() - 2);
59 | }
60 | return;
61 | }
62 | }
63 | if (ExtensionPanel.this.getTitleAt(ExtensionPanel.this.getSelectedIndex()).equals("...")) {
64 | tabCounter++;
65 | HackvertorPanel panel = new HackvertorPanel(hackvertor, true, false);
66 | ExtensionPanel.this.remove(ExtensionPanel.this.getSelectedIndex());
67 | ExtensionPanel.this.addTab(tabCounter + "", panel);
68 | ExtensionPanel.this.addTab("...", new JPanel());
69 | ExtensionPanel.this.setSelectedIndex(ExtensionPanel.this.getTabCount() - 2);
70 | }
71 | }
72 | });
73 | }
74 |
75 | public void refresh() {
76 | int index = ExtensionPanel.this.getSelectedIndex();
77 | if (index == -1) {
78 | return;
79 | }
80 | HackvertorPanel selectedPanel = (HackvertorPanel) getComponentAt(index);
81 | JTabbedPane tabs = selectedPanel.getTabs();
82 | int tabIndex = tabs.getSelectedIndex();
83 | String text = tabs.getTitleAt(tabIndex);
84 | if(text.equals("Custom")) {
85 | tabs.setComponentAt(tabIndex, TagUtils.createButtons(hackvertor.getTags(), selectedPanel.getInputArea(), Tag.Category.Custom, null, false));
86 | }
87 | }
88 |
89 | public HackvertorPanel addNewPanel(){
90 | HackvertorPanel panel = new HackvertorPanel(hackvertor, true, false);
91 | tabCounter++;
92 | this.insertTab(String.valueOf(tabCounter), null, panel, null, this.getTabCount() - 1);
93 | this.setSelectedIndex(this.getTabCount() - 2);
94 | return panel;
95 | }
96 |
97 | public void makeActiveBurpTab(){
98 | JTabbedPane tabbedPane = (JTabbedPane) this.getParent();
99 | tabbedPane.setSelectedComponent(this);
100 | }
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ui/HackvertorInput.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ui;
2 |
3 | import burp.hv.HackvertorExtension;
4 | import burp.IParameter;
5 | import burp.IRequestInfo;
6 |
7 | import javax.swing.*;
8 |
9 | import java.awt.*;
10 | import java.awt.event.KeyEvent;
11 | import java.awt.event.KeyListener;
12 | import java.awt.event.MouseAdapter;
13 | import java.awt.event.MouseEvent;
14 | import java.util.List;
15 |
16 | import static burp.hv.HackvertorExtension.callbacks;
17 | import static burp.hv.HackvertorExtension.helpers;
18 |
19 | public class HackvertorInput extends JTextArea {
20 | public HackvertorInput() {
21 | super();
22 | HackvertorInput that = this;
23 | this.addMouseListener(new MouseAdapter() {
24 | @Override
25 | public void mouseClicked(MouseEvent e) {
26 | if (e.getClickCount() == 2) { // Detect double-click
27 | int clickPos = that.viewToModel(e.getPoint());
28 | String text = that.getText();
29 | IRequestInfo analyzedRequest = helpers.analyzeRequest(helpers.stringToBytes(text));
30 | List params = analyzedRequest.getParameters();
31 |
32 | for (IParameter param : params) {
33 | int start = param.getValueStart();
34 | int end = param.getValueEnd();
35 |
36 | if (clickPos >= start && clickPos <= end) {
37 | that.select(start, end);
38 | }
39 | }
40 | }
41 | }
42 | });
43 | this.addKeyListener(new KeyListener() {
44 | @Override
45 | public void keyTyped(KeyEvent e) {
46 |
47 | }
48 |
49 | @Override
50 | public void keyPressed(KeyEvent e) {
51 | if ((e.getKeyCode() == KeyEvent.VK_PLUS || e.getKeyCode() == KeyEvent.VK_EQUALS) && (e.isMetaDown() || (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0)) {
52 | int fontSize = that.getFont().getSize();
53 | that.changeFontSize(fontSize + 1);
54 | } else if ((e.getKeyCode() == KeyEvent.VK_MINUS) && (e.isMetaDown() || (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0)) {
55 | int fontSize = that.getFont().getSize();
56 | that.changeFontSize(fontSize - 1);
57 | } else if ((e.isControlDown() || e.isMetaDown()) && (e.getKeyCode() == KeyEvent.VK_0)) {
58 | getFontSizeFromBurp();
59 | }
60 | }
61 |
62 | @Override
63 | public void keyReleased(KeyEvent e) {
64 |
65 | }
66 | });
67 | }
68 | public void updateUI() {
69 | super.updateUI();
70 | HackvertorExtension.isDarkTheme = HackvertorExtension.DARK_THEMES.contains(UIManager.getLookAndFeel().getID());
71 | SwingUtilities.invokeLater(() -> {
72 | getFontSizeFromBurp();
73 | });
74 | }
75 |
76 | public void getFontSizeFromBurp() {
77 | callbacks.customizeUiComponent(this);
78 | this.changeFontSize(this.getFont().getSize());
79 | }
80 |
81 | public void changeFontSize(int fontSize) {
82 | this.setFont(new Font("Courier New", Font.PLAIN, fontSize));
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ui/HackvertorMessageTab.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ui;
2 |
3 | import burp.hv.HackvertorExtension;
4 | import burp.hv.Hackvertor;
5 | import burp.IMessageEditorTab;
6 |
7 | import javax.swing.*;
8 | import javax.swing.event.DocumentEvent;
9 | import javax.swing.event.DocumentListener;
10 | import java.awt.*;
11 | import java.awt.event.HierarchyEvent;
12 |
13 | public class HackvertorMessageTab implements IMessageEditorTab {
14 | private final JPanel hackvertorContainer = new JPanel(new BorderLayout());
15 | private HackvertorPanel hackvertorPanel;
16 |
17 | private byte[] currentMessage;
18 | private Boolean changed = false;
19 | private Boolean interfaceCreated = false;
20 |
21 | public HackvertorMessageTab(Hackvertor hackvertor) {
22 | hackvertorContainer.addHierarchyListener(e -> {
23 | if(e.getChangeFlags() == HierarchyEvent.SHOWING_CHANGED) {
24 | if(e.getComponent() == hackvertorContainer && hackvertorContainer.isShowing()) {
25 | if(interfaceCreated) {
26 | SwingUtilities.invokeLater(() -> {
27 | hackvertorPanel.getInputArea().requestFocusInWindow();
28 | });
29 | return;
30 | }
31 | SwingUtilities.invokeLater(() -> {
32 | hackvertorPanel = new HackvertorPanel(hackvertor, false, true);
33 | hackvertorPanel.getInputArea().getDocument().addDocumentListener(new DocumentListener() {
34 | @Override
35 | public void insertUpdate(DocumentEvent e1) {
36 | changed = true;
37 | }
38 |
39 | @Override
40 | public void removeUpdate(DocumentEvent e1) {
41 | changed = true;
42 | }
43 |
44 | @Override
45 | public void changedUpdate(DocumentEvent e1) {
46 | changed = true;
47 | }
48 | });
49 | hackvertorContainer.add(hackvertorPanel);
50 | if (currentMessage != null) {
51 | hackvertorPanel.getInputArea().setText(HackvertorExtension.helpers.bytesToString(currentMessage));
52 | }
53 | interfaceCreated = true;
54 | hackvertorPanel.getInputArea().requestFocusInWindow();
55 | });
56 | }
57 | }
58 | });
59 | }
60 |
61 | @Override
62 | public String getTabCaption() {
63 | return "Hackvertor";
64 | }
65 |
66 | @Override
67 | public Component getUiComponent() {
68 | return hackvertorContainer;
69 | }
70 |
71 | @Override
72 | public boolean isEnabled(byte[] content, boolean isRequest) {
73 | return true;
74 | }
75 |
76 | @Override
77 | public void setMessage(byte[] content, boolean isRequest) {
78 | if (content == null) {
79 | changed = false;
80 | } else {
81 | if (hackvertorPanel != null) {
82 | hackvertorPanel.getInputArea().setText(HackvertorExtension.helpers.bytesToString(content));
83 | }
84 | }
85 | currentMessage = content;
86 | }
87 |
88 | @Override
89 | public byte[] getMessage() {
90 | if (changed) {
91 | return HackvertorExtension.helpers.stringToBytes(hackvertorPanel.getInputArea().getText());
92 | } else {
93 | return currentMessage;
94 | }
95 |
96 | }
97 |
98 | @Override
99 | public boolean isModified() {
100 | return changed;
101 | }
102 |
103 | @Override
104 | public byte[] getSelectedData() {
105 | return HackvertorExtension.helpers.stringToBytes(hackvertorPanel.getInputArea().getSelectedText());
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ui/JTabbedPaneClosable.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ui;
2 |
3 | import javax.swing.*;
4 | import java.awt.*;
5 | import java.awt.event.*;
6 |
7 | public class JTabbedPaneClosable extends JTabbedPane {
8 | public boolean clickedDelete = false;
9 |
10 | public JTabbedPaneClosable() {
11 | super();
12 | }
13 |
14 | @Override
15 | public void addTab(String title, Icon icon, Component component, String tip) {
16 | super.addTab(title, icon, component, tip);
17 | }
18 |
19 | @Override
20 | public void addTab(String title, Icon icon, Component component) {
21 | addTab(title, icon, component, null);
22 | }
23 |
24 | @Override
25 | public void addTab(String title, Component component) {
26 | addTab(title, null, component);
27 | }
28 |
29 | @Override
30 | public void insertTab(String title, Icon icon, Component component, String tip, int index) {
31 | super.insertTab(title, icon, component, tip, index);
32 | if (!title.equals("...")) {
33 | setTabComponentAt(index, new CloseButtonTab(component, title, icon));
34 | }
35 | }
36 |
37 | public void addTabNoExit(String title, Icon icon, Component component, String tip) {
38 | super.addTab(title, icon, component, tip);
39 | }
40 |
41 | public void addTabNoExit(String title, Icon icon, Component component) {
42 | addTabNoExit(title, icon, component, null);
43 | }
44 |
45 | public void addTabNoExit(String title, Component component) {
46 | addTabNoExit(title, null, component);
47 | }
48 |
49 | public class CloseButtonTab extends JPanel {
50 | private Component tab;
51 |
52 | public CloseButtonTab(final Component tab, String title, Icon icon) {
53 | this.tab = tab;
54 | setOpaque(false);
55 | setLayout(new GridBagLayout());
56 | GridBagConstraints c = new GridBagConstraints();
57 | c.fill = GridBagConstraints.HORIZONTAL;
58 | c.gridx = 0;
59 | c.gridy = 0;
60 | c.weightx = 0.5;
61 | c.gridwidth = 1;
62 | c.ipadx = 8;
63 | final JTextField textField = new JTextField(title);
64 | textField.setOpaque(false);
65 | textField.setBackground(new Color(0, 0, 0, 0));
66 | textField.setBorder(null);
67 | textField.setEditable(false);
68 | textField.addMouseListener(new MouseAdapter() {
69 | @Override
70 | public void mouseClicked(MouseEvent e) {
71 | if (e.getClickCount() == 2) {
72 | textField.setEditable(true);
73 | }
74 | if (e.getClickCount() == 1) {
75 | JTabbedPaneClosable tabbedPane = (JTabbedPaneClosable) textField.getParent().getParent().getParent();
76 | tabbedPane.setSelectedIndex(tabbedPane.indexOfComponent(tab));
77 | }
78 | }
79 | });
80 | textField.addFocusListener(new FocusAdapter() {
81 | public void focusLost(FocusEvent e) {
82 | textField.setEditable(false);
83 | }
84 | });
85 | add(textField, c);
86 | JLabel close = new JLabel("x");
87 | close.setFont(new Font("Courier", Font.PLAIN, 10));
88 | close.setPreferredSize(new Dimension(10, 10));
89 | close.setBorder(null);
90 | close.addMouseListener(new CloseListener(tab));
91 | c.gridx = 1;
92 | add(close, c);
93 | }
94 | }
95 |
96 | public class CloseListener implements MouseListener {
97 | private Component tab;
98 |
99 | public CloseListener(Component tab) {
100 | this.tab = tab;
101 | }
102 |
103 | @Override
104 | public void mouseClicked(MouseEvent e) {
105 | if (e.getSource() instanceof JLabel) {
106 | JLabel clickedButton = (JLabel) e.getSource();
107 | JTabbedPaneClosable tabbedPane = (JTabbedPaneClosable) clickedButton.getParent().getParent().getParent();
108 | clickedDelete = true;
109 | tabbedPane.remove(tab);
110 | }
111 | }
112 |
113 | @Override
114 | public void mousePressed(MouseEvent e) {
115 | }
116 |
117 | @Override
118 | public void mouseReleased(MouseEvent e) {
119 | }
120 |
121 | @Override
122 | public void mouseEntered(MouseEvent e) {
123 | if (e.getSource() instanceof JButton) {
124 | JButton clickedButton = (JButton) e.getSource();
125 | }
126 | }
127 |
128 | @Override
129 | public void mouseExited(MouseEvent e) {
130 | if (e.getSource() instanceof JButton) {
131 | JButton clickedButton = (JButton) e.getSource();
132 | }
133 | }
134 | }
135 | }
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ui/MontoyaContextMenu.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ui;
2 |
3 | import burp.api.montoya.MontoyaApi;
4 | import burp.api.montoya.core.ToolType;
5 | import burp.api.montoya.ui.contextmenu.ContextMenuEvent;
6 | import burp.api.montoya.ui.contextmenu.ContextMenuItemsProvider;
7 | import burp.hv.HackvertorExtension;
8 | import burp.hv.settings.InvalidTypeSettingException;
9 | import burp.hv.settings.UnregisteredSettingException;
10 |
11 | import javax.swing.*;
12 | import java.awt.*;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class MontoyaContextMenu implements ContextMenuItemsProvider {
17 | private final MontoyaApi api;
18 |
19 | public MontoyaContextMenu(MontoyaApi api)
20 | {
21 | this.api = api;
22 | }
23 |
24 | @Override
25 | public List provideMenuItems(ContextMenuEvent event)
26 | {
27 | boolean tagsInProxy;
28 | try {
29 | tagsInProxy = HackvertorExtension.generalSettings.getBoolean("tagsInProxy");
30 | } catch (UnregisteredSettingException | InvalidTypeSettingException e) {
31 | HackvertorExtension.callbacks.printError("Error loading settings:" + e);
32 | throw new RuntimeException(e);
33 | }
34 | if (event.isFromTool(ToolType.PROXY) && !tagsInProxy) {
35 | List menuItemList = new ArrayList<>();
36 | JMenuItem tagsInProxyItem = new JMenuItem("Tags in the proxy are disabled");
37 | tagsInProxyItem.addActionListener(e -> HackvertorExtension.alert("You can enable them in Hackvertor->Settings->Allow tags in proxy"));
38 | menuItemList.add(tagsInProxyItem);
39 | return menuItemList;
40 | }
41 |
42 | return null;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ui/SearchPanel.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ui;
2 |
3 | import burp.hv.Hackvertor;
4 | import burp.hv.utils.TagUtils;
5 |
6 | import javax.swing.*;
7 | import javax.swing.text.BadLocationException;
8 | import javax.swing.text.DefaultHighlighter;
9 | import javax.swing.text.Document;
10 | import javax.swing.text.Highlighter;
11 | import java.awt.*;
12 | import java.awt.event.KeyAdapter;
13 | import java.awt.event.KeyEvent;
14 | import java.util.regex.Matcher;
15 | import java.util.regex.Pattern;
16 | import java.util.regex.PatternSyntaxException;
17 |
18 | import static burp.hv.HackvertorExtension.isDarkTheme;
19 | import static burp.hv.HackvertorExtension.stderr;
20 |
21 | public class SearchPanel extends JPanel {
22 |
23 | private final Hackvertor hackvertor;
24 | private final HackvertorPanel hackvertorPanel;
25 |
26 | public SearchPanel(Hackvertor hackvertor, HackvertorPanel hackvertorPanel){
27 | super(new FlowLayout(FlowLayout.LEFT));
28 | this.hackvertor = hackvertor;
29 | this.hackvertorPanel = hackvertorPanel;
30 |
31 | this.setPreferredSize(new Dimension(1500, 80));
32 | String[] searchOptionsText = {"Search tags", "Search Input", "Search output"};
33 | JComboBox searchOptions = new JComboBox(searchOptionsText);
34 | JTextField searchBox = new JTextField();
35 | JCheckBox regexCheckbox = new JCheckBox("Regex?");
36 | searchBox.setPreferredSize(new Dimension(300, 30));
37 | JPanel tagsPanel = new JPanel();
38 | tagsPanel.setAutoscrolls(false);
39 | tagsPanel.setPreferredSize(new Dimension(1500, 50));
40 | searchBox.addKeyListener(new KeyAdapter() {
41 | public void keyReleased(KeyEvent e) {
42 |
43 | try {
44 | Pattern.compile(searchBox.getText());
45 | } catch (PatternSyntaxException ex) {
46 | stderr.println(ex);
47 | return;
48 | }
49 |
50 | if (searchOptions.getSelectedIndex() == 0) {
51 | searchTags(searchBox.getText(), tagsPanel, regexCheckbox.isSelected());
52 | } else if (searchOptions.getSelectedIndex() == 1) {
53 | search(searchBox.getText(), hackvertorPanel.getInputArea(), regexCheckbox.isSelected());
54 | } else if (searchOptions.getSelectedIndex() == 2) {
55 | search(searchBox.getText(), hackvertorPanel.getOutputArea(), regexCheckbox.isSelected());
56 | }
57 | }
58 | });
59 | this.add(searchOptions);
60 | this.add(searchBox);
61 | this.add(regexCheckbox);
62 | this.add(tagsPanel);
63 | }
64 |
65 | void searchTags(String input, JPanel tagsPanel, Boolean regex) {
66 | tagsPanel.removeAll();
67 | JScrollPane tags = TagUtils.createButtons(hackvertor.getTags(), hackvertorPanel.getInputArea(), null, input, regex);
68 | tags.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
69 | tags.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
70 | tags.setPreferredSize(new Dimension(1500, 40));
71 | tags.setBorder(null);
72 | tags.setAutoscrolls(false);
73 | tagsPanel.add(tags);
74 | tagsPanel.repaint();
75 | tagsPanel.validate();
76 | }
77 |
78 | void search(String findText, JTextArea element, Boolean regex) {
79 | try {
80 | Highlighter.HighlightPainter painter = new DefaultHighlighter.DefaultHighlightPainter(isDarkTheme ? Color.gray : Color.yellow);
81 | element.getHighlighter().removeAllHighlights();
82 | if (findText.length() == 0) {
83 | return;
84 | }
85 | int findLength = findText.length();
86 | Document doc = element.getDocument();
87 | String text = doc.getText(0, doc.getLength());
88 | int count = 0;
89 | int offset = 0;
90 | Pattern pattern = null;
91 | Matcher matcher = null;
92 | Boolean matched = false;
93 | if (regex) {
94 | pattern = Pattern.compile(findText);
95 | matcher = pattern.matcher(text);
96 | }
97 | while ((offset = regex ? (matcher.find() ? matcher.start() : -1) : text.indexOf(findText, offset)) != -1) {
98 | if (regex) {
99 | findLength = matcher.group().length();
100 | }
101 | element.select(offset, offset + findLength);
102 | element.getHighlighter().addHighlight(offset, offset + findLength, painter);
103 | offset += findLength;
104 | matched = true;
105 | count++;
106 | }
107 | if (!matched) {
108 | element.select(0, 0);
109 | }
110 | } catch (BadLocationException e) {
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/ui/SmartDecodeAction.java:
--------------------------------------------------------------------------------
1 | package burp.hv.ui;
2 |
3 | import burp.hv.Hackvertor;
4 |
5 | import javax.swing.*;
6 | import java.awt.event.ActionEvent;
7 |
8 | public class SmartDecodeAction extends AbstractAction {
9 | private final Hackvertor hackvertor;
10 | private final JTextArea inputArea;
11 | private final JTextArea outputArea;
12 | public SmartDecodeAction(JTextArea inputArea, JTextArea outputArea, Hackvertor hackvertor) {
13 | this.inputArea = inputArea;
14 | this.outputArea = outputArea;
15 | this.hackvertor = hackvertor;
16 | }
17 |
18 | public void actionPerformed(ActionEvent e) {
19 | if(inputArea.getSelectionStart() == inputArea.getSelectionEnd()) {
20 | return;
21 | }
22 | if(outputArea == null) {
23 | inputArea.replaceSelection(hackvertor.convert("<@auto_decode_no_decrypt>" + inputArea.getSelectedText() + "@auto_decode_no_decrypt>", hackvertor));
24 | } else {
25 | outputArea.replaceSelection(hackvertor.convert("<@auto_decode_no_decrypt>" + inputArea.getSelectedText() + "@auto_decode_no_decrypt>", hackvertor));
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/utils/GridbagUtils.java:
--------------------------------------------------------------------------------
1 | package burp.hv.utils;
2 |
3 | import java.awt.*;
4 |
5 | public class GridbagUtils {
6 | public static GridBagConstraints addMarginToGbc(GridBagConstraints gbc, int top, int left, int bottom, int right) {
7 | gbc.insets = new Insets(top, left, bottom, right);
8 | return gbc;
9 | }
10 |
11 | public static GridBagConstraints createConstraints(int x, int y, int gridWidth, int fill, double weightx, double weighty, int ipadx, int ipady, int anchor) {
12 | GridBagConstraints c = new GridBagConstraints();
13 | c.fill = fill;
14 | c.gridx = x;
15 | c.gridy = y;
16 | c.ipadx = ipadx;
17 | c.ipady = ipady;
18 | c.gridwidth = gridWidth;
19 | c.weightx = weightx;
20 | c.weighty = weighty;
21 | c.anchor = anchor;
22 | return c;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/utils/HttpUtils.java:
--------------------------------------------------------------------------------
1 | package burp.hv.utils;
2 |
3 | import burp.IParameter;
4 | import burp.IRequestInfo;
5 | import burp.hv.HackvertorExtension;
6 | import org.json.JSONArray;
7 | import org.json.JSONObject;
8 |
9 | import java.io.ByteArrayOutputStream;
10 | import java.io.IOException;
11 | import java.util.Arrays;
12 | import java.util.List;
13 |
14 | public class HttpUtils {
15 | public static int countMatches(byte[] response, byte[] match) {
16 | int matches = 0;
17 | if (match.length < 4) {
18 | return matches;
19 | }
20 |
21 | int start = 0;
22 | while (start < response.length) {
23 | start = HackvertorExtension.helpers.indexOf(response, match, true, start, response.length);
24 | if (start == -1)
25 | break;
26 | matches += 1;
27 | start += match.length;
28 | }
29 |
30 | return matches;
31 | }
32 |
33 | public static byte[] fixContentLength(byte[] request) {
34 | IRequestInfo analyzedRequest = HackvertorExtension.helpers.analyzeRequest(request);
35 | if (countMatches(request, HackvertorExtension.helpers.stringToBytes("Content-Length: ")) > 0) {
36 | int start = analyzedRequest.getBodyOffset();
37 | int contentLength = request.length - start;
38 | return setHeader(request, "Content-Length", Integer.toString(contentLength));
39 | } else {
40 | return request;
41 | }
42 | }
43 |
44 | public static int[] getHeaderOffsets(byte[] request, String header) {
45 | int i = 0;
46 | int end = request.length;
47 | while (i < end && request[i++] != '\n') {
48 | }
49 | while (i < end) {
50 | int line_start = i;
51 | while (i < end && request[i++] != ':') {
52 | }
53 | byte[] header_name = Arrays.copyOfRange(request, line_start, i - 1);
54 | int headerValueStart = i;
55 | while (i < end && request[i++] != '\n') {
56 | }
57 | if (i == end) {
58 | break;
59 | }
60 |
61 | String header_str = HackvertorExtension.helpers.bytesToString(header_name);
62 |
63 | if (header.equals(header_str)) {
64 | int[] offsets = {line_start, headerValueStart, i - 2};
65 | return offsets;
66 | }
67 |
68 | if (i + 2 < end && request[i] == '\r' && request[i + 1] == '\n') {
69 | break;
70 | }
71 | }
72 | return null;
73 | }
74 |
75 | public static byte[] setHeader(byte[] request, String header, String value) {
76 | int[] offsets = getHeaderOffsets(request, header);
77 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
78 | try {
79 | outputStream.write(Arrays.copyOfRange(request, 0, offsets[1]));
80 | outputStream.write(HackvertorExtension.helpers.stringToBytes(" " + value));
81 | outputStream.write(Arrays.copyOfRange(request, offsets[2], request.length));
82 | return outputStream.toByteArray();
83 | } catch (IOException e) {
84 | throw new RuntimeException("Request creation unexpectedly failed");
85 | } catch (NullPointerException e) {
86 | throw new RuntimeException("Can't find the header");
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/utils/TagUtils.java:
--------------------------------------------------------------------------------
1 | package burp.hv.utils;
2 |
3 | import burp.IContextMenuInvocation;
4 | import burp.IRequestInfo;
5 | import burp.hv.Convertors;
6 | import burp.hv.settings.InvalidTypeSettingException;
7 | import burp.hv.tags.Tag;
8 | import burp.hv.settings.UnregisteredSettingException;
9 | import burp.hv.ui.MenuScroller;
10 | import burp.parser.Element;
11 | import org.apache.commons.lang3.StringUtils;
12 |
13 | import javax.swing.*;
14 | import javax.swing.text.Highlighter;
15 | import java.awt.*;
16 | import java.awt.event.ActionListener;
17 | import java.io.ByteArrayOutputStream;
18 | import java.io.IOException;
19 | import java.util.Arrays;
20 | import java.util.Collection;
21 | import java.util.List;
22 | import java.util.Objects;
23 | import java.util.regex.Pattern;
24 | import java.util.stream.Collectors;
25 |
26 | import static burp.hv.HackvertorExtension.*;
27 |
28 | public class TagUtils {
29 | public static String paramRegex = "^[a-zA-Z_]\\w{0,10}$";
30 | public static String numberRegex = "^(?:0x[a-fA-F0-9]+|\\d+)$";
31 | public static String tagNameRegex = "[^\\w]";
32 |
33 | public static JScrollPane createButtons(List tags, final JTextArea inputArea, Tag.Category displayCategory, String searchTag, Boolean regex) {
34 | JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
35 | JScrollPane scrollFrame = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
36 |
37 | for (final Tag tagObj : tags) {
38 | final JButton btn = new JButton(tagObj.name);
39 | btn.setFocusable(false);
40 | btn.setToolTipText(tagObj.tooltip);
41 |
42 | if ((displayCategory != null && displayCategory.equals(tagObj.category)) || (StringUtils.isNotEmpty(searchTag) && (regex ? Pattern.compile(searchTag).matcher(tagObj.name).find() : tagObj.name.contains(searchTag)))) {
43 | if (!isNativeTheme && !isDarkTheme) {
44 | btn.setBackground(Color.decode("#005a70"));
45 | btn.setForeground(Color.white);
46 | }
47 | btn.putClientProperty("tag", tagObj);
48 | btn.addActionListener(e -> {
49 | String selectedText = inputArea.getSelectedText();
50 | if (selectedText == null) {
51 | selectedText = "";
52 | }
53 | String[] tagStartEnd = Convertors.generateTagStartEnd(tagObj);
54 | String tagStart = tagStartEnd[0];
55 | String tagEnd = tagStartEnd[1];
56 | String replacedText = tagStart + selectedText + tagEnd;
57 | int start = inputArea.getSelectionStart();
58 | int end = start + replacedText.length();
59 | inputArea.replaceSelection(replacedText);
60 | inputArea.select(start + tagStart.length(), end - tagEnd.length());
61 | int selectionStart = inputArea.getSelectionStart();
62 | int selectionEnd = inputArea.getSelectionEnd();
63 | Highlighter.Highlight[] highlights = inputArea.getHighlighter().getHighlights();
64 | for (Highlighter.Highlight highlight : highlights) {
65 | int highlightStart = highlight.getStartOffset();
66 | int highlightEnd = highlight.getEndOffset();
67 | if ((highlightStart < selectionEnd && highlightEnd > selectionStart)) {
68 | continue;
69 | }
70 | inputArea.select(highlight.getStartOffset(), highlight.getEndOffset());
71 | selectedText = inputArea.getSelectedText();
72 | if (selectedText != null) {
73 | tagStartEnd = Convertors.generateTagStartEnd(tagObj);
74 | tagStart = tagStartEnd[0];
75 | tagEnd = tagStartEnd[1];
76 | inputArea.replaceSelection(tagStart + selectedText + tagEnd);
77 | }
78 | }
79 | });
80 | panel.add(btn);
81 | }
82 | }
83 | return scrollFrame;
84 | }
85 |
86 | public static String elementSequenceToString(List elements){
87 | return elements.stream().map(Objects::toString).collect(Collectors.joining());
88 | }
89 |
90 | public static Tag getTagByTagName(Collection tags, String tagName) {
91 | return tags.stream().filter(tag -> tagName.equals(tag.name)).findFirst().orElse(null);
92 | }
93 |
94 | public static ActionListener generateTagActionListener(final IContextMenuInvocation invocation, Tag tagObj) {
95 | return e -> {
96 | boolean allowTagCount;
97 | try {
98 | allowTagCount = generalSettings.getBoolean("allowTagCount");
99 | } catch (UnregisteredSettingException | InvalidTypeSettingException ex) {
100 | callbacks.printError("Error loading settings:" + e);
101 | throw new RuntimeException(ex);
102 | }
103 | String[] tagStartEnd = Convertors.generateTagStartEnd(tagObj);
104 | String tagStart = tagStartEnd[0];
105 | String tagEnd = tagStartEnd[1];
106 | if (invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_EDITOR_REQUEST || invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_MESSAGE_VIEWER_REQUEST || invocation.getInvocationContext() == IContextMenuInvocation.CONTEXT_INTRUDER_PAYLOAD_POSITIONS) {
107 | int[] bounds = invocation.getSelectionBounds();
108 | byte[] message = invocation.getSelectedMessages()[0].getRequest();
109 | if(allowTagCount) {
110 | IRequestInfo analyzedRequest = helpers.analyzeRequest(message);
111 | String context = Utils.getContext(analyzedRequest);
112 | if(contextTagCount.containsKey(context)) {
113 | int currentCount = contextTagCount.get(context).get(tagObj.name) == null ? 0 : contextTagCount.get(context).get(tagObj.name);
114 | contextTagCount.get(context).put(tagObj.name, currentCount + 1);
115 | }
116 |
117 | int count = tagCount.get(tagObj.name) == null ? 0 : tagCount.get(tagObj.name);
118 | tagCount.put(tagObj.name, count + 1);
119 | }
120 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
121 | try {
122 | outputStream.write(Arrays.copyOfRange(message, 0, bounds[0]));
123 | outputStream.write(helpers.stringToBytes(tagStart));
124 | outputStream.write(Arrays.copyOfRange(message, bounds[0], bounds[1]));
125 | outputStream.write(helpers.stringToBytes(tagEnd));
126 | outputStream.write(Arrays.copyOfRange(message, bounds[1], message.length));
127 | outputStream.flush();
128 | invocation.getSelectedMessages()[0].setRequest(outputStream.toByteArray());
129 | } catch (IOException e1) {
130 | System.err.println(e1.toString());
131 | }
132 | }
133 | };
134 | }
135 |
136 | public static JMenu createTagMenuForCategory(List tags, Tag.Category category, final IContextMenuInvocation invocation, String searchTag, Boolean regex, Tag specificTag) {
137 | JMenu parentMenu = new JMenu(category.name());
138 | int tagCount = (int) tags.stream().filter(tag -> tag.category == category).count();
139 | if (tagCount > 40) {
140 | JMenu numberMenu = new JMenu("0-9");
141 | MenuScroller.setScrollerFor(numberMenu);
142 | parentMenu.add(numberMenu);
143 | for (char c = 'a'; c <= 'z'; c++) {
144 | JMenu letterMenu = new JMenu(String.valueOf(c));
145 | MenuScroller.setScrollerFor(letterMenu);
146 | parentMenu.add(letterMenu);
147 | }
148 | }
149 |
150 | for (final Tag tagObj : tags) {
151 | final JMenuItem menu = new JMenuItem(tagObj.name);
152 | menu.setToolTipText(tagObj.tooltip);
153 | if ((category != null && category.equals(tagObj.category)) || (searchTag.length() > 0 && (regex ? tagObj.name.matches(searchTag) : tagObj.name.contains(searchTag)))) {
154 | menu.addActionListener(generateTagActionListener(invocation, tagObj));
155 | if (tagCount > 40) {
156 | for (int i = 0; i < parentMenu.getItemCount(); i++) {
157 | if (parentMenu.getItem(i).getText().equals("0-9") && Character.isDigit(tagObj.name.charAt(0))) {
158 | parentMenu.getItem(i).add(menu);
159 | } else if (tagObj.name.toLowerCase().startsWith(parentMenu.getItem(i).getText())) {
160 | parentMenu.getItem(i).add(menu);
161 | }
162 | }
163 | } else {
164 | parentMenu.add(menu);
165 | }
166 | }
167 | }
168 | return parentMenu;
169 | }
170 |
171 | public static String sanitizeTagName(String tagName) {
172 | return tagName.replaceAll(tagNameRegex, "");
173 | }
174 |
175 | public static Boolean validateParam(String param) {
176 | return param.matches(paramRegex);
177 | }
178 |
179 | public static Boolean validateCode(String code) {
180 | return !code.isEmpty();
181 | }
182 |
183 | public static Boolean validateCodeLength(String code) {
184 | return !code.isEmpty();
185 | }
186 |
187 | public static Boolean validateTagName(String code) {
188 | code = sanitizeTagName(code);
189 | return !code.isEmpty();
190 | }
191 |
192 | public static Boolean validateTagParamNumber(String tagParamNumber) {
193 | return tagParamNumber.matches(numberRegex);
194 | }
195 |
196 | public static String getExtensionFromLanguage(String language) {
197 | switch (language) {
198 | case "AI":
199 | return ".ai";
200 | case "Python":
201 | return ".py";
202 | case "JavaScript":
203 | return ".js";
204 | case "Java":
205 | return ".java";
206 | case "Groovy":
207 | return ".groovy";
208 | default:
209 | return null;
210 | }
211 | }
212 | }
213 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/utils/UrlUtils.java:
--------------------------------------------------------------------------------
1 | package burp.hv.utils;
2 |
3 | import java.net.URL;
4 |
5 | public class UrlUtils {
6 | public static String buildUrl(URL url) {
7 | int port = url.getPort();
8 | StringBuilder urlResult = new StringBuilder();
9 | urlResult.append(url.getProtocol());
10 | urlResult.append(":");
11 | if (url.getAuthority() != null && !url.getAuthority().isEmpty()) {
12 | urlResult.append("//");
13 | urlResult.append(url.getHost());
14 | }
15 |
16 | if ((url.getProtocol().equals("http") && port != 80) || (url.getProtocol().equals("https") && port != 443) && port != -1) {
17 | urlResult.append(':').append(port);
18 | }
19 | if (url.getPath() != null) {
20 | urlResult.append(url.getPath());
21 | }
22 | if (url.getQuery() != null) {
23 | urlResult.append("?");
24 | urlResult.append(url.getQuery());
25 | }
26 | if (url.getRef() != null) {
27 | urlResult.append("#");
28 | urlResult.append(url.getRef());
29 | }
30 | return urlResult.toString();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/java/burp/hv/utils/Utils.java:
--------------------------------------------------------------------------------
1 | package burp.hv.utils;
2 |
3 | import burp.IRequestInfo;
4 | import burp.hv.tags.CustomTags;
5 | import burp.hv.settings.Settings;
6 | import burp.hv.tags.TagStore;
7 | import burp.hv.Variables;
8 | import burp.hv.ui.HackvertorInput;
9 |
10 | import javax.swing.*;
11 | import javax.swing.border.Border;
12 | import javax.swing.border.EmptyBorder;
13 | import java.awt.*;
14 | import java.awt.event.ActionEvent;
15 | import java.awt.event.ActionListener;
16 | import java.awt.event.WindowAdapter;
17 | import java.awt.event.WindowEvent;
18 | import java.io.IOException;
19 | import java.net.URI;
20 | import java.net.URISyntaxException;
21 |
22 | import static burp.hv.HackvertorExtension.*;
23 |
24 | public class Utils {
25 |
26 | public static String getContext(IRequestInfo analyzedRequest) {
27 | if(analyzedRequest == null) {
28 | return null;
29 | }
30 | if(analyzedRequest.getContentType() == IRequestInfo.CONTENT_TYPE_JSON) {
31 | return "JSON";
32 | }
33 | if(analyzedRequest.getMethod() != null && analyzedRequest.getMethod().equalsIgnoreCase("GET")) {
34 | return "GET";
35 | }
36 | if(analyzedRequest.getMethod() != null && analyzedRequest.getMethod().equalsIgnoreCase("POST")) {
37 | return "POST";
38 | }
39 | return null;
40 | }
41 |
42 | public static void setMarginAndPadding(JComponent comp, int amount) {
43 | Border margin = new EmptyBorder(amount,amount,amount,amount);
44 | comp.setBorder(margin);
45 | }
46 |
47 | public static void openUrl(String url) {
48 | if(url.startsWith("https://")) {
49 | if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
50 | try {
51 | Desktop.getDesktop().browse(new URI(url));
52 | } catch (IOException ioException) {
53 | } catch (URISyntaxException uriSyntaxException) {
54 |
55 | }
56 | }
57 | }
58 | }
59 |
60 | public static String escapeChar(String input, String chr) {
61 | return input.replaceAll("(["+chr+"])", "\\\\$1");
62 | }
63 |
64 | public static void registerGeneralSettings(Settings settings) {
65 | settings.registerBooleanSetting("tagsInProxy", false, "Allow tags in Proxy", "Tag permissions", null);
66 | settings.registerBooleanSetting("tagsInIntruder", true, "Allow tags in Intruder", "Tag permissions", null);
67 | settings.registerBooleanSetting("tagsInRepeater", true, "Allow tags in Repeater", "Tag permissions", null);
68 | settings.registerBooleanSetting("tagsInScanner", true, "Allow tags in Scanner", "Tag permissions", null);
69 | settings.registerBooleanSetting("tagsInExtensions", true, "Allow tags in Extensions", "Tag permissions", null);
70 | settings.registerBooleanSetting("codeExecutionTagsEnabled", false, "Allow code execution tags", "Tag permissions", "Using code execution tags on untrusted requests can compromise your system, are you sure?");
71 | settings.registerBooleanSetting("autoUpdateContentLength", true, "Auto update content length", "Requests", null);
72 | settings.registerBooleanSetting("allowTagCount", true, "Count tag usage (Not sent to any server)","Statistics", null);
73 | settings.registerBooleanSetting("allowAiToGenerateCode", false, "Use AI to generate code", "AI", "Using AI to generate code execution tags can compromise your system, be careful when using it with untrusted repeater requests. Are you sure?");
74 | settings.registerBooleanSetting("allowAiToSummariseCode", false, "Use AI to summarise custom tags code", "AI", null);
75 | settings.registerBooleanSetting("allowAiTags", false, "Use AI in tags", "AI", null);
76 | settings.registerBooleanSetting("debugAi", false, "Debug AI requests", "AI", null);
77 | settings.registerBooleanSetting("learnFromRepeater", false, "Use AI to learn from repeater", "AI", "This is experimental. It will send your entire repeater requests to the AI in order to learn encodings. Are you sure you want to enable this?");
78 | settings.registerBooleanSetting("sortTagCategories", true, "Alphabetically sort tag categories", "Misc", null);
79 | settings.registerBooleanSetting("allowAutoConvertClipboard", false, "Auto convert clipboard","Misc", null);
80 |
81 | }
82 |
83 | public static JFrame getHackvertorWindowInstance() {
84 | if(HackvertorFrame != null) {
85 | return HackvertorFrame;
86 | }
87 | HackvertorFrame = new JFrame();
88 | HackvertorFrame.addWindowListener(new WindowAdapter() {
89 | @Override
90 | public void windowClosing(WindowEvent e) {
91 | HackvertorFrame.setVisible(false);
92 | HackvertorFrame.getContentPane().removeAll();
93 | HackvertorFrame.getContentPane().setLayout(new BorderLayout());
94 | }
95 | });
96 | return HackvertorFrame;
97 | }
98 |
99 | public static JMenu generateHackvertorMenuBar() {
100 | JMenu hvMenuBar = new JMenu("Hackvertor");
101 | JMenuItem createCustomTagsMenu = new JMenuItem("Create custom tag");
102 | createCustomTagsMenu.addActionListener(new ActionListener() {
103 | @Override
104 | public void actionPerformed(ActionEvent e) {
105 | CustomTags.showCreateEditTagDialog(false, null);
106 | }
107 | });
108 | JMenuItem listCustomTagsMenu = new JMenuItem("List custom tags");
109 | listCustomTagsMenu.addActionListener(new ActionListener() {
110 | @Override
111 | public void actionPerformed(ActionEvent e) {
112 | CustomTags.showListTagsDialog();
113 | }
114 | });
115 | JMenuItem tagStoreMenu = new JMenuItem("View tag store");
116 | tagStoreMenu.addActionListener(new ActionListener() {
117 | @Override
118 | public void actionPerformed(ActionEvent e) {
119 | TagStore.showTagStore();
120 | }
121 | });
122 | JMenuItem globalVariablesMenu = new JMenuItem("Global variables");
123 | globalVariablesMenu.addActionListener(new ActionListener() {
124 | @Override
125 | public void actionPerformed(ActionEvent e) {
126 | Variables.showGlobalVariablesWindow();
127 | }
128 | });
129 | hvMenuBar.add(createCustomTagsMenu);
130 | hvMenuBar.add(listCustomTagsMenu);
131 | hvMenuBar.add(globalVariablesMenu);
132 | hvMenuBar.addSeparator();
133 | hvMenuBar.add(tagStoreMenu);
134 | JMenuItem settingsMenu = new JMenuItem("Settings");
135 | settingsMenu.addActionListener(e -> Settings.showSettingsWindow());
136 | hvMenuBar.addSeparator();
137 | hvMenuBar.add(settingsMenu);
138 | hvMenuBar.addSeparator();
139 | JMenuItem reportBugMenu = new JMenuItem("Report bug/request feature");
140 | reportBugMenu.addActionListener(e -> {
141 | Utils.openUrl("https://github.com/hackvertor/hackvertor/issues/new");
142 | });
143 | hvMenuBar.add(reportBugMenu);
144 | return hvMenuBar;
145 | }
146 |
147 | public static void configureTextArea(HackvertorInput area) {
148 | area.setLineWrap(true);
149 | callbacks.customizeUiComponent(area);
150 | }
151 |
152 | public static boolean hasApiMethod(Object obj, String methodName) {
153 | try {
154 | Class> clazz = obj.getClass();
155 | clazz.getMethod(methodName);
156 | return true;
157 | } catch(NoSuchMethodException e){
158 | return false;
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/src/main/javacc/burp/parser/Element.java:
--------------------------------------------------------------------------------
1 | package burp.parser;
2 |
3 | import org.apache.commons.lang3.StringUtils;
4 | import org.unbescape.java.JavaEscape;
5 |
6 | import java.util.ArrayList;
7 |
8 | public class Element {
9 |
10 | public static class StartTag extends Element {
11 | String identifier;
12 | ArrayList arguments;
13 | public StartTag(String identifier, ArrayList arguments) {
14 | this.identifier = identifier;
15 | this.arguments = arguments;
16 | }
17 |
18 | public String getIdentifier() {
19 | return identifier;
20 | }
21 |
22 | public ArrayList getArguments() {
23 | return arguments;
24 | }
25 |
26 | @Override
27 | public String toString() {
28 | StringBuilder sb = new StringBuilder();
29 | sb.append("<@" + identifier);
30 | if(arguments.size() > 0){
31 | sb.append("(");
32 | for (int i = 0; i < arguments.size(); i++) {
33 | if(i != 0) sb.append(",");
34 | String argument = arguments.get(i);
35 | if((argument.startsWith("0x") && argument.matches("^0x[0-9a-fA-F]+$")) || (argument.startsWith("-") && StringUtils.isNumeric(argument.substring(1))) || StringUtils.isNumeric(argument) || argument.equals("true") || argument.equals("false"))
36 | sb.append(argument);
37 | else
38 | sb.append("'" + JavaEscape.escapeJava(argument) + "'");
39 | }
40 | sb.append(")");
41 | }
42 | sb.append(">");
43 | return sb.toString();
44 | }
45 | }
46 |
47 | public static class SelfClosingTag extends StartTag {
48 | public SelfClosingTag(String identifier, ArrayList arguments){
49 | super(identifier, arguments);
50 | }
51 |
52 | @Override
53 | public String toString() {
54 | StringBuilder sb = new StringBuilder();
55 | sb.append("<@" + identifier);
56 | if(arguments.size() > 0){
57 | sb.append("(");
58 | for (int i = 0; i < arguments.size(); i++) {
59 | if(i != 0) sb.append(",");
60 | String argument = arguments.get(i);
61 | if((argument.startsWith("-") && StringUtils.isNumeric(argument.substring(1))) || StringUtils.isNumeric(argument) || argument.equals("true") || argument.equals("false")) {
62 | sb.append(argument);
63 | } else {
64 | sb.append("\"").append(arguments.get(i)).append("\"");
65 | }
66 | }
67 | sb.append(")");
68 | }
69 | sb.append("/>");
70 | return sb.toString();
71 | }
72 | }
73 |
74 | public static class EndTag extends Element {
75 | String identifier;
76 |
77 | public EndTag(String identifier) {
78 | this.identifier = identifier;
79 | }
80 |
81 | public String getIdentifier() {
82 | return identifier;
83 | }
84 |
85 | @Override
86 | public String toString() {
87 | return "@" + identifier + ">";
88 | }
89 | }
90 |
91 | public static class TextElement extends Element{
92 | String content;
93 | public TextElement(String text){
94 | this.content = text;
95 | }
96 |
97 | public String getContent() {
98 | return content;
99 | }
100 |
101 | public void setContent(String content) {
102 | this.content = content;
103 | }
104 |
105 | @Override
106 | public String toString() {
107 | return content;
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/javacc/burp/parser/parser.jj:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | */
4 |
5 | options {
6 | IGNORE_CASE = true;
7 | STATIC = false;
8 | DEBUG_TOKEN_MANAGER = false;
9 | UNICODE_INPUT = true;
10 | }
11 |
12 | PARSER_BEGIN(HackvertorParser)
13 | package burp.parser;
14 |
15 | import java.io.StringReader;
16 | import java.util.LinkedList;
17 | import java.util.ArrayList;
18 | import org.unbescape.java.JavaEscape;
19 |
20 | public class HackvertorParser {
21 |
22 | private static String getTokenText(Token first, Token cur) {
23 | Token t;
24 | StringBuffer sb = new StringBuffer();
25 |
26 | for (t=first; t != cur.next; t = t.next) {
27 | if (t.specialToken != null) {
28 | Token tt=t.specialToken;
29 | while (tt.specialToken != null)
30 | tt = tt.specialToken;
31 | for (; tt != null; tt = tt.next)
32 | sb.append(tt.image);
33 | };
34 | sb.append(t.image);
35 | };
36 | return sb.toString();
37 | }
38 |
39 | public static LinkedList parse(String string) throws ParseException {
40 | HackvertorParser parser = new HackvertorParser(new StringReader(string));
41 | LinkedList elementList = parser.Input();
42 | // for (Element e : elementList) {
43 | // System.out.println(e.getClass() + " - " + e.toString());
44 | // }
45 | return elementList;
46 | }
47 | }
48 |
49 | PARSER_END(HackvertorParser)
50 |
51 | TOKEN_MGR_DECLS : {
52 | // required by SetState
53 | void backup(int n) {
54 | input_stream.backup(n);
55 | }
56 | }
57 |
58 | <*> SKIP: { "=\r" | "=\r\n" }
59 | <*> TOKEN [IGNORE_CASE]: {
60 | <#IDENTIFIER: (["0"-"9", "a"-"z", "A"-"Z","_", "-"])+ >
61 | | <#QUOTED_STRING: ( "'" ("\\" ~[] | ~["'", "\\"] )* "'" ) | ( "\"" ("\\" ~[] | ~["\"", "\\"] )* "\"" ) >
62 | | <#LITERAL: (["0"-"9", "a"-"z", "A"-"Z", "-", "+", "."])+>
63 | | <#WHITESPACE: ( " " | "\t" ) >
64 | }
65 |
66 | <*> TOKEN : {
67 | : StartTag
68 | | : StartTag
69 | }
70 |
71 | TOKEN: {
72 | //Anything excluding <
73 | |
74 | }
75 |
76 | TOKEN: {
77 | > : InsideTag
78 | | : DEFAULT
79 | }
80 |
81 | // SPECIAL_TOKEN :
82 | //{
83 | // < ()+ >
84 | //}
85 |
86 | TOKEN : {
87 | : Args
88 | | "> : DEFAULT
89 | | "> : DEFAULT
90 | | " > : DEFAULT
91 | | : DEFAULT
92 | }
93 |
94 | TOKEN : {
95 | >
96 | | >
97 | |
98 | | : InsideTag
99 | | : DEFAULT
100 | }
101 |
102 | <*> TOKEN : { }
103 |
104 | LinkedList Input() :
105 | {
106 | LinkedList s = new LinkedList();
107 | LinkedList e;
108 | }
109 | {
110 | (
111 | e = ElementSequence() { s.addAll(e); }
112 | )*
113 |
114 | { return s; }
115 | }
116 |
117 | LinkedList ElementSequence() :
118 | {
119 | LinkedList elements = new LinkedList();
120 | Element e;
121 | Token text;
122 | Token firstToken = getToken(1);
123 | }
124 | {
125 | try{
126 | elements = StartTag() { return elements; }
127 | | elements = CloseTag() { return elements; }
128 | | { elements.add(new Element.TextElement("<")); return elements;}
129 | | (
130 | text = | text = | text = | text = | text =
131 | | text = | text = | text = | text = | text =
132 | ) { elements.add(new Element.TextElement(text.image)); return elements; }
133 | }catch(ParseException ex){ //Catch any unexpected inputs including EOF and try to recover
134 | token_source.SwitchTo(DEFAULT);
135 | elements.addFirst(new Element.TextElement(getTokenText(firstToken, getToken(0))));
136 | elements.addAll(ElementSequence());
137 | return elements;
138 | }
139 | }
140 |
141 | LinkedList StartTag() :
142 | {
143 | LinkedList elements = new LinkedList();
144 | ArrayList args = new ArrayList();
145 | LinkedList rest = null;
146 | Token t;
147 | Token identifier = null;
148 | String arg;
149 | Token firstToken = getToken(1);
150 | }
151 | {
152 | try{
153 | t= identifier =
154 | [
155 |
156 |
157 | [
158 | arg = Argument() {args.add(arg);}
159 | ( arg = Argument() {args.add(arg);})*
160 | ]
161 |
162 |
163 | ]
164 | (
165 | {elements.add(new Element.StartTag(identifier.image, args)); return elements;}
166 | | (|) {elements.add(new Element.SelfClosingTag(identifier.image, args)); return elements;}
167 | )
168 | }catch(ParseException e){
169 | // System.out.println("Failed Start tag. Treating as text");
170 | elements.addFirst(new Element.TextElement(getTokenText(firstToken, getToken(0))));
171 | }
172 | [LOOKAHEAD(2) rest = ElementSequence()] { if(rest != null) elements.addAll(rest); return elements; }
173 | }
174 |
175 | String Argument() :
176 | {
177 | Token t;
178 | }
179 | {
180 | t = {return JavaEscape.unescapeJava(t.image.substring(1, t.image.length() - 1));}
181 | | t = {return t.image;}
182 | }
183 |
184 | LinkedList CloseTag() :
185 | {
186 | LinkedList elements = new LinkedList();
187 | LinkedList rest = null;
188 | Token t;
189 | Token firstToken = getToken(1);
190 | }
191 | {
192 | try{
193 | t= { elements.add(new Element.EndTag(t.image)); return elements; }
194 | }catch(ParseException e){
195 | // System.out.println("Failed End tag. Treating as text");
196 | elements.addFirst(new Element.TextElement(getTokenText(firstToken, getToken(0))));
197 | }
198 | [LOOKAHEAD(2) rest = ElementSequence()] { if(rest != null) elements.addAll(rest); return elements; }
199 | }
--------------------------------------------------------------------------------
/src/main/resources/bigrams.txt:
--------------------------------------------------------------------------------
1 | TH 116997844
2 | HE 100689263
3 | IN 87674002
4 | ER 77134382
5 | AN 69775179
6 | RE 60923600
7 | ES 57070453
8 | ON 56915252
9 | ST 54018399
10 | NT 50701084
11 | EN 48991276
12 | AT 48274564
13 | ED 46647960
14 | ND 46194306
15 | TO 46115188
16 | OR 45725191
17 | EA 43329810
18 | TI 42888666
19 | AR 42353262
20 | TE 42295813
21 | NG 38567365
22 | AL 38211584
23 | IT 37938534
24 | AS 37773878
25 | IS 37349981
26 | HA 35971841
27 | ET 32872552
28 | SE 31532272
29 | OU 31112284
30 | OF 30540904
31 | LE 30383262
32 | SA 30080131
33 | VE 29320973
34 | RO 29230770
35 | RA 28645577
36 | RI 27634643
37 | HI 27495342
38 | NE 27331675
39 | ME 27237733
40 | DE 27029835
41 | CO 26737101
42 | TA 26147593
43 | EC 25775798
44 | SI 25758841
45 | LL 24636875
46 | SO 23903631
47 | NA 23547524
48 | LI 23291169
49 | LA 23178317
50 | EL 23092248
51 | MA 21828378
52 | DI 21673998
53 | IC 21468412
54 | RT 21456059
55 | NS 21306421
56 | RS 21237259
57 | IO 21210160
58 | OM 21066156
59 | CH 20132750
60 | OT 20088048
61 | CA 19930754
62 | CE 19803619
63 | HO 19729026
64 | BE 19468489
65 | TT 19367472
66 | FO 18923772
67 | TS 18922522
68 | SS 18915696
69 | NO 18894111
70 | EE 18497942
71 | EM 18145294
72 | AC 17904683
73 | IL 17877600
74 | DA 17584055
75 | NI 17452104
76 | UR 17341717
77 | WA 16838794
78 | SH 16773127
79 | EI 16026915
80 | AM 15975981
81 | TR 15821226
82 | DT 15759673
83 | US 15699353
84 | LO 15596310
85 | PE 15573318
86 | UN 15237699
87 | NC 15214623
88 | WI 15213018
89 | UT 15137169
90 | AD 14877234
91 | EW 14776406
92 | OW 14610429
93 | GE 14425023
94 | EP 14024377
95 | AI 13974919
96 | LY 13742031
97 | OL 13726491
98 | FT 13696078
99 | OS 13596265
100 | EO 13524186
101 | EF 13252227
102 | PR 13191182
103 | WE 13185116
104 | DO 13120322
105 | MO 12950768
106 | ID 12896787
107 | IE 12505546
108 | MI 12168944
109 | PA 12068709
110 | FI 11993833
111 | PO 11917535
112 | CT 11888752
113 | WH 11852909
114 | IR 11681353
115 | AY 11523416
116 | GA 11239788
117 | SC 10800636
118 | KE 10650670
119 | EV 10574011
120 | SP 10570626
121 | IM 10544422
122 | OP 10459455
123 | DS 10429887
124 | LD 10245579
125 | UL 10173468
126 | OO 10168856
127 | SU 10031005
128 | IA 10002012
129 | GH 9880399
130 | PL 9812226
131 | EB 9738798
132 | IG 9530574
133 | VI 9380037
134 | IV 9129232
135 | WO 9106647
136 | YO 9088497
137 | RD 9025637
138 | TW 8910254
139 | BA 8867461
140 | AG 8809266
141 | RY 8788539
142 | AB 8775582
143 | LS 8675452
144 | SW 8673234
145 | AP 8553911
146 | FE 8529289
147 | TU 8477495
148 | CI 8446084
149 | FA 8357929
150 | HT 8351551
151 | FR 8339376
152 | AV 8288885
153 | EG 8286463
154 | GO 8188708
155 | BO 8172395
156 | BU 8113271
157 | TY 8008918
158 | MP 7835172
159 | OC 7646952
160 | OD 7610214
161 | EH 7559141
162 | YS 7539621
163 | EY 7528342
164 | RM 7377989
165 | OV 7350014
166 | GT 7347990
167 | YA 7239548
168 | CK 7205091
169 | GI 7103140
170 | RN 7064635
171 | GR 6989963
172 | RC 6974063
173 | BL 6941044
174 | LT 6817273
175 | YT 6714151
176 | OA 6554221
177 | YE 6499305
178 | OB 6212512
179 | DB 6106719
180 | FF 6085519
181 | SF 6073995
182 | RR 5896212
183 | DU 5861311
184 | KI 5814357
185 | UC 5742385
186 | IF 5740414
187 | AF 5702567
188 | DR 5701879
189 | CL 5683204
190 | EX 5649363
191 | SM 5580755
192 | PI 5559210
193 | SB 5553684
194 | CR 5514347
195 | TL 5403137
196 | OI 5336616
197 | RU 5330557
198 | UP 5306948
199 | BY 5232074
200 | TC 5196817
201 | NN 5180899
202 | AK 5137311
203 | SL 4965012
204 | NF 4950333
205 | UE 4927837
206 | DW 4906814
207 | AU 4884168
208 | PP 4873393
209 | UG 4832325
210 | RL 4803246
211 | RG 4645938
212 | BR 4621080
213 | CU 4604045
214 | UA 4589997
215 | DH 4585765
216 | RK 4491400
217 | YI 4461214
218 | LU 4402940
219 | UM 4389720
220 | BI 4356462
221 | NY 4343290
222 | NW 4215967
223 | QU 4169424
224 | OG 4163126
225 | SN 4157990
226 | MB 4121764
227 | VA 4111375
228 | DF 4033878
229 | DD 4001275
230 | MS 3922855
231 | GS 3920675
232 | AW 3918960
233 | NH 3915410
234 | PU 3858148
235 | HR 3843001
236 | SD 3842250
237 | TB 3815459
238 | PT 3812475
239 | NM 3796928
240 | DC 3782481
241 | GU 3768430
242 | TM 3759861
243 | MU 3755834
244 | NU 3732602
245 | MM 3730508
246 | NL 3692985
247 | EU 3674130
248 | WN 3649615
249 | NB 3602692
250 | RP 3588188
251 | DM 3544905
252 | SR 3513808
253 | UD 3499535
254 | UI 3481482
255 | RF 3436232
256 | OK 3397570
257 | YW 3379064
258 | TF 3368452
259 | IP 3348621
260 | RW 3348005
261 | RB 3346212
262 | OH 3254659
263 | KS 3227333
264 | DP 3145043
265 | FU 3138900
266 | YC 3128053
267 | TP 3070427
268 | MT 3055946
269 | DL 3050945
270 | NK 3043200
271 | CC 3026492
272 | UB 2990868
273 | RH 2968706
274 | NP 2968126
275 | JU 2924815
276 | FL 2890839
277 | DN 2840522
278 | KA 2833038
279 | PH 2825344
280 | HU 2771830
281 | JO 2721345
282 | LF 2702522
283 | YB 2696786
284 | RV 2692445
285 | OE 2616308
286 | IB 2598444
287 | IK 2585124
288 | YP 2581863
289 | GL 2576787
290 | LP 2543957
291 | YM 2516273
292 | LB 2463693
293 | HS 2462026
294 | DG 2442139
295 | GN 2426429
296 | EK 2411639
297 | NR 2393580
298 | PS 2377036
299 | TD 2346516
300 | LC 2328063
301 | SK 2321888
302 | YF 2305244
303 | YH 2291273
304 | VO 2253292
305 | AH 2225270
306 | DY 2218040
307 | LM 2216514
308 | SY 2214270
309 | NV 2194534
310 | YD 2122337
311 | FS 2047416
312 | SG 2043770
313 | YR 2021939
314 | YL 2013939
315 | WS 1988727
316 | MY 1949129
317 | OY 1932892
318 | KN 1903836
319 | IZ 1865802
320 | XP 1840696
321 | LW 1836811
322 | TN 1782119
323 | KO 1758001
324 | AA 1721143
325 | JA 1712763
326 | ZE 1709871
327 | FC 1570791
328 | GW 1567991
329 | TG 1530045
330 | XT 1509969
331 | FH 1507604
332 | LR 1505092
333 | JE 1487348
334 | YN 1485655
335 | GG 1468286
336 | GF 1465290
337 | EQ 1461436
338 | HY 1446451
339 | KT 1443985
340 | HC 1441057
341 | BS 1409672
342 | HW 1403223
343 | HN 1383958
344 | CS 1381608
345 | HM 1353001
346 | NJ 1342735
347 | HH 1329998
348 | WT 1301293
349 | GC 1299541
350 | LH 1274048
351 | EJ 1256993
352 | FM 1251312
353 | DV 1238565
354 | LV 1238287
355 | WR 1226755
356 | GP 1215204
357 | FP 1199845
358 | GB 1184377
359 | GM 1178511
360 | HL 1169468
361 | LK 1164186
362 | CY 1145316
363 | MC 1101727
364 | YG 1049082
365 | XI 1024736
366 | HB 1014004
367 | FW 1005903
368 | GY 979804
369 | HP 978649
370 | MW 937621
371 | PM 931225
372 | ZA 929119
373 | LG 926472
374 | IW 922059
375 | XA 904148
376 | FB 888155
377 | SV 882083
378 | GD 879792
379 | IX 879360
380 | AJ 870262
381 | KL 846309
382 | HF 834284
383 | HD 828755
384 | AE 815963
385 | SQ 800346
386 | DJ 799366
387 | FY 789961
388 | AZ 768359
389 | LN 752316
390 | AO 749566
391 | FD 748027
392 | KW 719633
393 | MF 715087
394 | MH 710864
395 | SJ 704442
396 | UF 701892
397 | TV 698150
398 | XC 697995
399 | YU 695512
400 | BB 689158
401 | WW 674610
402 | OJ 661082
403 | AX 660826
404 | MR 660619
405 | WL 657782
406 | XE 653947
407 | KH 650095
408 | OX 650078
409 | UO 649906
410 | ZI 644035
411 | FG 637758
412 | IH 610683
413 | TK 610333
414 | II 607124
415 | IU 576683
416 | TJ 559473
417 | MN 558397
418 | WY 553647
419 | KY 553296
420 | KF 537342
421 | FN 534362
422 | UY 531960
423 | PW 530411
424 | DK 525744
425 | RJ 518157
426 | UK 514873
427 | KR 507020
428 | KU 506618
429 | WM 505687
430 | KM 485617
431 | MD 481126
432 | ML 478528
433 | EZ 465466
434 | KB 457860
435 | WC 448394
436 | WD 432646
437 | HG 429607
438 | BT 428276
439 | ZO 424016
440 | KC 420017
441 | PF 418168
442 | YV 411487
443 | PC 400308
444 | PY 396147
445 | WB 394820
446 | YK 391953
447 | CP 382923
448 | YJ 378679
449 | KP 375653
450 | PB 369336
451 | CD 358435
452 | JI 357577
453 | UW 352732
454 | UH 339341
455 | WF 336213
456 | YY 332973
457 | WP 321746
458 | BC 320380
459 | AQ 315068
460 | CB 298053
461 | IQ 291635
462 | CM 285942
463 | MG 285133
464 | DQ 283314
465 | BJ 282608
466 | TZ 280007
467 | KD 277982
468 | PD 273162
469 | FJ 269865
470 | CF 267630
471 | NZ 266461
472 | CW 257253
473 | FV 244685
474 | VY 233082
475 | FK 228905
476 | OZ 228556
477 | ZZ 221275
478 | IJ 219128
479 | LJ 218362
480 | NQ 217422
481 | UV 212051
482 | XO 211173
483 | PG 211133
484 | HK 210385
485 | KG 209266
486 | VS 204093
487 | HV 197539
488 | BM 191807
489 | HJ 189906
490 | CN 188046
491 | GV 186777
492 | CG 181590
493 | WU 180884
494 | GJ 176947
495 | XH 166599
496 | GK 163830
497 | TQ 159111
498 | CQ 157546
499 | RQ 156933
500 | BH 154489
501 | XS 154347
502 | UZ 153736
503 | WK 148964
504 | XU 147533
505 | UX 144814
506 | BD 141752
507 | BW 140189
508 | WG 139890
509 | MV 136314
510 | MJ 134263
511 | PN 131645
512 | XM 127492
513 | OQ 122677
514 | BV 120081
515 | XW 119322
516 | KK 118811
517 | BP 115161
518 | ZU 113538
519 | RZ 113432
520 | XF 113031
521 | MK 111041
522 | ZH 107639
523 | BN 106125
524 | ZY 105871
525 | HQ 101241
526 | WJ 99435
527 | IY 98361
528 | DZ 98038
529 | VR 96416
530 | ZS 94993
531 | XY 94329
532 | CV 94224
533 | XB 94041
534 | XR 90046
535 | UJ 88168
536 | YQ 87953
537 | VD 85611
538 | PK 83017
539 | VU 82830
540 | JR 80471
541 | ZL 80039
542 | SZ 79840
543 | YZ 78281
544 | LQ 77148
545 | KJ 76816
546 | BF 75352
547 | NX 74844
548 | QA 73527
549 | QI 73387
550 | KV 73184
551 | ZW 68865
552 | WV 63930
553 | UU 63043
554 | VT 62912
555 | VP 62577
556 | XD 60101
557 | GQ 59750
558 | XL 59585
559 | VC 59024
560 | CZ 57914
561 | LZ 57314
562 | ZT 56955
563 | WZ 52836
564 | SX 50975
565 | ZB 50652
566 | VL 49032
567 | PV 48105
568 | FQ 47504
569 | PJ 47043
570 | ZM 46034
571 | VW 45608
572 | CJ 41526
573 | ZC 41037
574 | BG 40516
575 | JS 39326
576 | XG 39289
577 | RX 38654
578 | HZ 37066
579 | XX 35052
580 | VM 35024
581 | XN 34734
582 | QW 34669
583 | JP 34520
584 | VN 33082
585 | ZD 32906
586 | ZR 32685
587 | FZ 31186
588 | XV 31117
589 | ZP 30389
590 | VH 30203
591 | VB 29192
592 | ZF 28658
593 | GZ 28514
594 | TX 28156
595 | VF 28090
596 | DX 27413
597 | QB 27307
598 | BK 26993
599 | ZG 26369
600 | VG 25585
601 | JC 24770
602 | ZK 24262
603 | ZN 24241
604 | UQ 23386
605 | JM 22338
606 | VV 22329
607 | JD 21903
608 | MQ 21358
609 | JH 20960
610 | QS 20847
611 | JT 20408
612 | JB 19380
613 | FX 19313
614 | PQ 18607
615 | MZ 18271
616 | YX 16945
617 | QT 16914
618 | WQ 16245
619 | JJ 16085
620 | JW 16083
621 | LX 15467
622 | GX 14778
623 | JN 14452
624 | ZV 14339
625 | MX 14250
626 | JK 13967
627 | KQ 13905
628 | XK 13651
629 | JF 12640
630 | QM 12315
631 | QH 12273
632 | JL 12149
633 | JG 12023
634 | VK 11469
635 | VJ 11432
636 | KZ 11192
637 | QC 10667
638 | XJ 10629
639 | PZ 9697
640 | QL 9603
641 | QO 9394
642 | JV 8925
643 | QF 8778
644 | QD 8678
645 | BZ 8132
646 | HX 7526
647 | ZJ 7167
648 | PX 6814
649 | QP 6062
650 | QE 6020
651 | QR 5975
652 | ZQ 5773
653 | JY 5723
654 | BQ 5513
655 | XQ 5416
656 | CX 5300
657 | KX 5083
658 | WX 4678
659 | QY 4557
660 | QV 4212
661 | QN 3808
662 | VX 3192
663 | BX 3021
664 | JZ 2859
665 | VZ 2633
666 | QG 2567
667 | QQ 2499
668 | ZX 2463
669 | XZ 2082
670 | QK 2023
671 | VQ 1488
672 | QJ 1342
673 | QX 765
674 | JX 747
675 | JQ 722
676 | QZ 280
--------------------------------------------------------------------------------
/src/main/resources/images/logo-dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PortSwigger/hackvertor/48bd4571c825fa3181efb6658830a1da5241b662/src/main/resources/images/logo-dark.png
--------------------------------------------------------------------------------
/src/main/resources/images/logo-light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PortSwigger/hackvertor/48bd4571c825fa3181efb6658830a1da5241b662/src/main/resources/images/logo-light.png
--------------------------------------------------------------------------------
/src/main/resources/images/logo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PortSwigger/hackvertor/48bd4571c825fa3181efb6658830a1da5241b662/src/main/resources/images/logo.gif
--------------------------------------------------------------------------------
/src/main/resources/monograms.txt:
--------------------------------------------------------------------------------
1 | E 529117365
2 | T 390965105
3 | A 374061888
4 | O 326627740
5 | I 320410057
6 | N 313720540
7 | S 294300210
8 | R 277000841
9 | H 216768975
10 | L 183996130
11 | D 169330528
12 | C 138416451
13 | U 117295780
14 | M 110504544
15 | F 95422055
16 | G 91258980
17 | P 90376747
18 | W 79843664
19 | Y 75294515
20 | B 70195826
21 | V 46337161
22 | K 35373464
23 | J 9613410
24 | X 8369915
25 | Z 4975847
26 | Q 4550166
--------------------------------------------------------------------------------
/src/test/java/TestExtension.java:
--------------------------------------------------------------------------------
1 | import burp.hv.HackvertorExtension;
2 | import burp.stubs.StubCallbacks;
3 |
4 | import javax.swing.*;
5 | import java.awt.*;
6 |
7 | public class TestExtension {
8 |
9 | public static void main(String[] args) {
10 | JFrame jFrame = new JFrame("Burp Suite - Hackvertor");
11 | jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12 | jFrame.setContentPane(new JPanel(new BorderLayout()));
13 | jFrame.setPreferredSize(new Dimension(900,800));
14 | JMenuBar menuBar = new JMenuBar();
15 | jFrame.setJMenuBar(menuBar);
16 | HackvertorExtension hackvertorExtension = new HackvertorExtension();
17 | hackvertorExtension.registerExtenderCallbacks(new StubCallbacks(jFrame));
18 | jFrame.pack();
19 | jFrame.setVisible(true);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/test/java/burp/ConvertorTests.java:
--------------------------------------------------------------------------------
1 | package burp;
2 | import burp.hv.Hackvertor;
3 | import burp.hv.HackvertorExtension;
4 | import burp.stubs.StubExtensionHelpers;
5 | import hv.parser.ParseException;
6 | import org.junit.jupiter.api.Test;
7 |
8 | import javax.swing.*;
9 |
10 | import static org.junit.jupiter.api.Assertions.assertEquals;
11 |
12 | public class ConvertorTests {
13 |
14 | private final Hackvertor hackvertor;
15 |
16 | public ConvertorTests() {
17 | JFrame jFrame = new JFrame("Burp Suite - Hackvertor");
18 | HackvertorExtension hvExtension = new HackvertorExtension();
19 | hvExtension.registerExtenderCallbacks(new burp.stubs.StubCallbacks(jFrame));
20 | this.hackvertor = new Hackvertor();
21 | HackvertorExtension.setHelpers(new StubExtensionHelpers());
22 | }
23 |
24 | @Test
25 | void convertSpaceInTag() throws ParseException {
26 | String spaceInContent = "<@base64> @base64>";
27 | String converted = hackvertor.convert(spaceInContent, hackvertor);
28 | assertEquals("IA==", converted);
29 | }
30 |
31 | //Test for #92.
32 | @Test
33 | void testSpaceInAttribute(){
34 | String plaintext = "<@ascii2hex('')>abcd@ascii2hex>";
35 | assertEquals("61626364", hackvertor.convert(plaintext, hackvertor));
36 | plaintext = "<@ascii2hex(' ')>abcd@ascii2hex>";
37 | assertEquals("61 62 63 64", hackvertor.convert(plaintext, hackvertor));
38 | plaintext = "<@ascii2hex(' ')>abcd@ascii2hex>";
39 | assertEquals("61 62 63 64", hackvertor.convert(plaintext, hackvertor));
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/test/java/burp/parser/HackvertorParserTest.java:
--------------------------------------------------------------------------------
1 | package burp.parser;
2 | import org.junit.jupiter.api.Test;
3 |
4 | import java.util.LinkedList;
5 |
6 | import static org.junit.jupiter.api.Assertions.*;
7 |
8 | class HackvertorParserTest {
9 |
10 | @Test
11 | void parseUnicode1() {
12 | assertDoesNotThrow(() -> {
13 | HackvertorParser.parse("你好世界");
14 | });
15 | }
16 | @Test
17 | void parseUnicode2() {
18 | assertDoesNotThrow(() -> {
19 | HackvertorParser.parse("’");
20 | });
21 | }
22 |
23 | @Test
24 | void parseSpaces() throws ParseException {
25 | String spaceInContent = "<@base64> @base64>";
26 | LinkedList parsed = HackvertorParser.parse(spaceInContent);
27 | assertEquals(3, parsed.size());
28 | assertInstanceOf(Element.TextElement.class, parsed.get(1));
29 | assertEquals(" ", parsed.get(1).toString());
30 | }
31 | }
--------------------------------------------------------------------------------
/src/test/java/burp/stubs/StubExtensionHelpers.java:
--------------------------------------------------------------------------------
1 | package burp.stubs;
2 |
3 | import burp.*;
4 | import org.python.apache.xerces.impl.dv.util.Base64;
5 |
6 | import java.net.URL;
7 | import java.util.List;
8 |
9 | public class StubExtensionHelpers implements IExtensionHelpers {
10 | @Override
11 | public IRequestInfo analyzeRequest(IHttpRequestResponse request) {
12 | return null;
13 | }
14 |
15 | @Override
16 | public IRequestInfo analyzeRequest(IHttpService httpService, byte[] request) {
17 | return null;
18 | }
19 |
20 | @Override
21 | public IRequestInfo analyzeRequest(byte[] request) {
22 | return null;
23 | }
24 |
25 | @Override
26 | public IResponseInfo analyzeResponse(byte[] response) {
27 | return null;
28 | }
29 |
30 | @Override
31 | public IParameter getRequestParameter(byte[] request, String parameterName) {
32 | return null;
33 | }
34 |
35 | @Override
36 | public String urlDecode(String data) {
37 | return null;
38 | }
39 |
40 | @Override
41 | public String urlEncode(String data) {
42 | return null;
43 | }
44 |
45 | @Override
46 | public byte[] urlDecode(byte[] data) {
47 | return new byte[0];
48 | }
49 |
50 | @Override
51 | public byte[] urlEncode(byte[] data) {
52 | return new byte[0];
53 | }
54 |
55 | @Override
56 | public byte[] base64Decode(String data) {
57 | return new byte[0];
58 | }
59 |
60 | @Override
61 | public byte[] base64Decode(byte[] data) {
62 | return base64Decode(data);
63 | }
64 |
65 | @Override
66 | public String base64Encode(String data) {
67 | return Base64.encode(data.getBytes());
68 | }
69 |
70 | @Override
71 | public String base64Encode(byte[] data) {
72 | return null;
73 | }
74 |
75 | @Override
76 | public byte[] stringToBytes(String data) {
77 | return new byte[0];
78 | }
79 |
80 | @Override
81 | public String bytesToString(byte[] data) {
82 | return null;
83 | }
84 |
85 | @Override
86 | public int indexOf(byte[] data, byte[] pattern, boolean caseSensitive, int from, int to) {
87 | return 0;
88 | }
89 |
90 | @Override
91 | public byte[] buildHttpMessage(List headers, byte[] body) {
92 | return new byte[0];
93 | }
94 |
95 | @Override
96 | public byte[] buildHttpRequest(URL url) {
97 | return new byte[0];
98 | }
99 |
100 | @Override
101 | public byte[] addParameter(byte[] request, IParameter parameter) {
102 | return new byte[0];
103 | }
104 |
105 | @Override
106 | public byte[] removeParameter(byte[] request, IParameter parameter) {
107 | return new byte[0];
108 | }
109 |
110 | @Override
111 | public byte[] updateParameter(byte[] request, IParameter parameter) {
112 | return new byte[0];
113 | }
114 |
115 | @Override
116 | public byte[] toggleRequestMethod(byte[] request) {
117 | return new byte[0];
118 | }
119 |
120 | @Override
121 | public IHttpService buildHttpService(String host, int port, String protocol) {
122 | return null;
123 | }
124 |
125 | @Override
126 | public IHttpService buildHttpService(String host, int port, boolean useHttps) {
127 | return null;
128 | }
129 |
130 | @Override
131 | public IParameter buildParameter(String name, String value, byte type) {
132 | return null;
133 | }
134 |
135 | @Override
136 | public IScannerInsertionPoint makeScannerInsertionPoint(String insertionPointName, byte[] baseRequest, int from, int to) {
137 | return null;
138 | }
139 |
140 | @Override
141 | public IResponseVariations analyzeResponseVariations(byte[]... responses) {
142 | return null;
143 | }
144 |
145 | @Override
146 | public IResponseKeywords analyzeResponseKeywords(List keywords, byte[]... responses) {
147 | return null;
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/tag-store/README.md:
--------------------------------------------------------------------------------
1 | # Hackvertor tag store
2 |
3 | Welcome to the Hackvertor tag store. This repository allows Hackvertor users to submit their custom tags to be shared with other users. The store can be viewed in the Hackvertor menu->View tag store.
4 |
5 | ## Rules of submission
6 |
7 | 1. Only one tag per pull request is allowed.
8 | 2. There should only be one file with your code you cannot import multiple files.
9 | 3. The maximum size of your code is 1337 bytes.
10 | 4. You should create a folder with your tag name and the file should also have the same name.
11 | 5. The author property should reflect your github username.
12 | 6. Any form of obfuscation will be rejected.
13 | 7. Any form of malicious code is obviously not allowed.
14 | 8. You must test your tag before submission
15 |
16 | ## How to submit your custom tag
17 |
18 | 1. Create a custom tag in the usual way.
19 | 2. Test and make sure it works in the Hackvertor interface.
20 | 3. Edit the custom tag and click "Export to tag store"
21 | 4. Fork the Hackvertor github repo.
22 | 5. Paste the JSON into the tag-store.json file
23 | 6. Create a folder with your tag name in the tag-store folder
24 | 7. Create your file inside the folder with the same tag name with the extension
25 | 8. Create a pull request.
26 | 9. As long as you've followed the rules above the submission will be reviewed and merged
27 | 10. Submission approval may take some time as I have a day job :)
--------------------------------------------------------------------------------
/tag-store/base62_decode/base62_decode.py:
--------------------------------------------------------------------------------
1 | charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
2 | base = 62
3 |
4 | def _value(ch, charset):
5 | try:
6 | return charset.index(ch)
7 | except ValueError:
8 | raise ValueError("base62: Invalid character (%s)" % ch)
9 |
10 | def decode(encoded):
11 | l, i, v = len(encoded), 0, 0
12 | for x in encoded:
13 | v += _value(x, charset=charset) * (base ** (l - (i + 1)))
14 | i += 1
15 | return v
16 |
17 | def decodebytes(encoded):
18 |
19 | leading_null_bytes = b""
20 | while encoded.startswith("0") and len(encoded) >= 2:
21 | leading_null_bytes += b"\x00" * _value(encoded[1], charset)
22 | encoded = encoded[2:]
23 | decoded = decode(encoded)
24 | buf = bytearray()
25 | while decoded > 0:
26 | buf.append(decoded & 0xFF)
27 | decoded //= 256
28 | buf.reverse()
29 |
30 | return leading_null_bytes + bytes(buf)
31 |
32 | output = decodebytes(input)
--------------------------------------------------------------------------------
/tag-store/base62_encode/base62_encode.py:
--------------------------------------------------------------------------------
1 | charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
2 | base = 62
3 |
4 | def _value(ch, charset):
5 | try:
6 | return charset.index(ch)
7 | except ValueError:
8 | raise ValueError("base62: Invalid character (%s)" % ch)
9 |
10 | def b62_encode(n):
11 | chs = []
12 | while n > 0:
13 | n, r = divmod(n, base)
14 | chs.insert(0, charset[r])
15 | if not chs:
16 | return "0"
17 | return "".join(chs)
18 |
19 | def encodebytes(barray):
20 | barray = bytes(barray)
21 | leading_zeros_count = 0
22 | for i in range(len(barray)):
23 | if barray[i] != 0:
24 | break
25 | leading_zeros_count += 1
26 | n, r = divmod(leading_zeros_count, len(charset) - 1)
27 | zero_padding = "0{}".format(charset[-1]) * n
28 | if r:
29 | zero_padding += "0{}".format(charset[r])
30 | if leading_zeros_count == len(barray):
31 | return zero_padding
32 | value = b62_encode(int(barray.encode('hex'), 16))
33 | return zero_padding + value
34 |
35 | output = encodebytes(input)
--------------------------------------------------------------------------------
/tag-store/ean13/ean13.py:
--------------------------------------------------------------------------------
1 | # Takes a number as an input and ignores dots in the input, then calculates EAN13 checksum, see https://en.wikipedia.org/wiki/International_Article_Number
2 | # the "append" parameter can be set to 0 or 1.
3 | # Examples:
4 | # <@_ean13(1,'[...]')>756.9217.0769.8@_ean13> -> 756.9217.0769.85
5 | # <@_ean13(0,'[...]')>756.9217.0769.8@_ean13> -> 5
6 | # Was tested with de-facto Swiss Social Security numbers (AHV/AVS numbers)
7 | # see also https://www.pentagrid.ch/en/blog/burp-suite-hackvertor-custom-tags-email-sms-tan-multi-factor-authentication/
8 | z=input
9 | y=z.replace('.','')
10 | checksum=str(10-(sum([3*int(x) for x in y[1:][::-2]])+sum([int(x) for x in y[::-1][1::2]]))%10)[-1]
11 | output = z+checksum if append else checksum
12 |
--------------------------------------------------------------------------------
/tag-store/email_utf7/email_utf7.groovy:
--------------------------------------------------------------------------------
1 | output = input.length() == 0 ? ""
2 | : "&" + input.replaceAll("(.)","\u0000\$0")
3 | .bytes.encodeBase64().toString()
4 | .replaceAll(/=+$/,"") + "-";
--------------------------------------------------------------------------------
/tag-store/email_utf7_decode/email_utf7_decode.groovy:
--------------------------------------------------------------------------------
1 | output = input.replaceAll("&[a-zA-Z0-9]+-", { match ->
2 | return new String(match.replaceAll("[&-]","").decodeBase64())
3 | })
--------------------------------------------------------------------------------
/tag-store/encode_word_meta/encode_word_meta.js:
--------------------------------------------------------------------------------
1 | output = "=?"+charset+"?q?"+input+"?=";
--------------------------------------------------------------------------------
/tag-store/encoded_word_decode/encoded_word_decode.js:
--------------------------------------------------------------------------------
1 | //Ugh regex isn't working in the JS engine I use :(
2 | function isHexChar(char) {
3 | return (char >= '0' && char <= '9') || (char >= 'A' && char <= 'F') || (char >= 'a' && char <= 'f');
4 | }
5 |
6 | let parts = input.replaceAll("_"," ").split('=');
7 | output = parts.slice(1).reduce((str, part) =>
8 | str + (isHexChar(part[0]) && isHexChar(part[1]) ? String.fromCodePoint(parseInt(part.slice(0, 2), 16)) : part.slice(0, 2)) + part.slice(2),
9 | parts[0]);
--------------------------------------------------------------------------------
/tag-store/encoded_word_encode/encoded_word_encode.js:
--------------------------------------------------------------------------------
1 | output = input.split('').map(chr => '=' + chr.codePointAt().toString(16).padStart(2, '0')).join('')
--------------------------------------------------------------------------------
/tag-store/hello_world/hello_world.py:
--------------------------------------------------------------------------------
1 | output = input + strHello + str(intTest)
--------------------------------------------------------------------------------
/tag-store/ip/ip.js:
--------------------------------------------------------------------------------
1 | output = (Math.floor(Math.random() * 255) + 1)+"."+(Math.floor(Math.random() * 255))+"."+(Math.floor(Math.random() * 255))+"."+(Math.floor(Math.random() * 255))
--------------------------------------------------------------------------------
/tag-store/tag-store.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "tagName": "base62_encode",
4 | "description": "Performs a base62 encode.",
5 | "author": "H1v0lt4g3",
6 | "numberOfArgs": 0,
7 | "language": "Python"
8 | },
9 | {
10 | "tagName": "base62_decode",
11 | "description": "Performs a base62 decode.",
12 | "author": "H1v0lt4g3",
13 | "numberOfArgs": 0,
14 | "language": "Python"
15 | },
16 | {
17 | "tagName": "hello_world",
18 | "description": "An example tag that shows the JSON structure.",
19 | "author": "hackvertor",
20 | "numberOfArgs": 2,
21 | "argument1": "strHello",
22 | "argument1Type": "String",
23 | "argument1Default": "Hello world",
24 | "argument2": "intTest",
25 | "argument2Type": "Number",
26 | "argument2Default": "1337",
27 | "language": "Python"
28 | },
29 | {
30 | "tagName": "ean13",
31 | "description": "EAN13 checksum",
32 | "author": "pentagridsec",
33 | "numberOfArgs": 1,
34 | "argument1": "append",
35 | "argument1Type": "Number",
36 | "argument1Default": "1",
37 | "argument2": "notused",
38 | "argument2Type": "String",
39 | "argument2Default": "",
40 | "language": "Python"
41 | },
42 | {
43 | "tagName": "totp",
44 | "description": "Create Time-based OTPs on the fly, secret as input",
45 | "author": "pentagridsec",
46 | "numberOfArgs": 0,
47 | "language": "Python"
48 | },
49 | {
50 | "tagName": "ip",
51 | "description": "Generates a random IP",
52 | "author": "Techbrunch",
53 | "numberOfArgs": 0,
54 | "language": "JavaScript"
55 | },
56 | {
57 | "argument1Default": "4",
58 | "author": "Techbrunch",
59 | "argument1Type": "String",
60 | "numberOfArgs": 1,
61 | "argument1": "version",
62 | "description": "Generates a UUID",
63 | "language": "Python",
64 | "tagName": "uuid",
65 | "argument2": "notused",
66 | "argument2Type": "String",
67 | "argument2Default": ""
68 | },
69 | {
70 | "argument1Default": "0x100",
71 | "author": "hackvertor",
72 | "argument1Type": "Number",
73 | "numberOfArgs": 1,
74 | "argument1": "mask",
75 | "description": "This tag creates a unicode overflow.",
76 | "language": "JavaScript",
77 | "tagName": "unicode_overflow",
78 | "argument2": "notused",
79 | "argument2Type": "String",
80 | "argument2Default": ""
81 | },
82 | {
83 | "argument1Default": "0xfff",
84 | "author": "hackvertor",
85 | "argument1Type": "Number",
86 | "numberOfArgs": 1,
87 | "argument1": "max",
88 | "description": "This tag created multiple unicode overflows",
89 | "language": "JavaScript",
90 | "tagName": "unicode_overflow_variations",
91 | "argument2": "notused",
92 | "argument2Type": "String",
93 | "argument2Default": ""
94 | },
95 | {
96 | "description": "Encodes Q encoded word",
97 | "language": "JavaScript",
98 | "tagName": "encoded_word_encode",
99 | "author": "hackvertor",
100 | "numberOfArgs": 0
101 | },
102 | {
103 | "description": "Decode Q encoded word",
104 | "language": "JavaScript",
105 | "tagName": "encoded_word_decode",
106 | "author": "hackvertor",
107 | "numberOfArgs": 0
108 | },
109 | {
110 | "description": "UTF-7 email encoder",
111 | "language": "Groovy",
112 | "tagName": "email_utf7",
113 | "author": "hackvertor",
114 | "numberOfArgs": 0
115 | },
116 | {
117 | "argument1Default": "iso-8859-1",
118 | "author": "hackvertor",
119 | "argument1Type": "String",
120 | "numberOfArgs": 1,
121 | "argument1": "charset",
122 | "description": "Creates encoded word meta data",
123 | "language": "JavaScript",
124 | "tagName": "encode_word_meta",
125 | "argument2": "notused",
126 | "argument2Type": "String",
127 | "argument2Default": ""
128 | },
129 | {
130 | "description": "Decodes email based UTF-7",
131 | "language": "Groovy",
132 | "tagName": "email_utf7_decode",
133 | "author": "hackvertor",
134 | "numberOfArgs": 0
135 | }
136 | ]
137 |
--------------------------------------------------------------------------------
/tag-store/totp/totp.py:
--------------------------------------------------------------------------------
1 | ####
2 | # BIG DISCLAIMER
3 | # I only copied the relevant parts of https://pypi.org/project/pyotp/
4 | # The code there is MIT License (MIT License)
5 | # Unfortunately Hackvertor can't do Python type hinting, sad panda, removed all type hinting
6 | # START OF COPIED CODE
7 | ####
8 |
9 | import calendar
10 | import datetime
11 | import hashlib
12 | import time
13 | import hmac
14 | import base64
15 | import unicodedata
16 |
17 | def strings_equal(s1, s2):
18 | """
19 | Timing-attack resistant string comparison.
20 |
21 | Normal comparison using == will short-circuit on the first mismatching
22 | character. This avoids that by scanning the whole string, though we
23 | still reveal to a timing attack whether the strings are the same
24 | length.
25 | """
26 | s1 = unicodedata.normalize("NFKC", s1)
27 | s2 = unicodedata.normalize("NFKC", s2)
28 | return hmac.compare_digest(s1.encode("utf-8"), s2.encode("utf-8"))
29 |
30 |
31 | class OTP(object):
32 | """
33 | Base class for OTP handlers.
34 | """
35 |
36 | def __init__(
37 | self,
38 | s,
39 | digits = 6,
40 | digest = hashlib.sha1,
41 | name = None,
42 | issuer = None,
43 | ):
44 | self.digits = digits
45 | if digits > 10:
46 | raise ValueError("digits must be no greater than 10")
47 | self.digest = digest
48 | self.secret = s
49 | self.name = name or "Secret"
50 | self.issuer = issuer
51 |
52 | def generate_otp(self, input):
53 | """
54 | :param input: the HMAC counter value to use as the OTP input.
55 | Usually either the counter, or the computed integer based on the Unix timestamp
56 | """
57 | if input < 0:
58 | raise ValueError("input must be positive integer")
59 | hasher = hmac.new(self.byte_secret(), self.int_to_bytestring(input), self.digest)
60 | hmac_hash = bytearray(hasher.digest())
61 | offset = hmac_hash[-1] & 0xF
62 | code = (
63 | (hmac_hash[offset] & 0x7F) << 24
64 | | (hmac_hash[offset + 1] & 0xFF) << 16
65 | | (hmac_hash[offset + 2] & 0xFF) << 8
66 | | (hmac_hash[offset + 3] & 0xFF)
67 | )
68 | str_code = str(10000000000 + (code % 10**self.digits))
69 | return str_code[-self.digits :]
70 |
71 | def byte_secret(self):
72 | secret = self.secret
73 | missing_padding = len(secret) % 8
74 | if missing_padding != 0:
75 | secret += "=" * (8 - missing_padding)
76 | return base64.b32decode(secret, casefold=True)
77 |
78 | @staticmethod
79 | def int_to_bytestring(i, padding = 8):
80 | """
81 | Turns an integer to the OATH specified
82 | bytestring, which is fed to the HMAC
83 | along with the secret
84 | """
85 | result = bytearray()
86 | while i != 0:
87 | result.append(i & 0xFF)
88 | i >>= 8
89 | # It's necessary to convert the final result from bytearray to bytes
90 | # because the hmac functions in python 2.6 and 3.3 don't work with
91 | # bytearray
92 | return bytes(bytearray(reversed(result)).rjust(padding, b"\0"))
93 |
94 |
95 | class TOTP(OTP):
96 | """
97 | Handler for time-based OTP counters.
98 | """
99 |
100 | def __init__(
101 | self,
102 | s,
103 | digits = 6,
104 | digest = None,
105 | name = None,
106 | issuer = None,
107 | interval = 30,
108 | ):
109 | """
110 | :param s: secret in base32 format
111 | :param interval: the time interval in seconds for OTP. This defaults to 30.
112 | :param digits: number of integers in the OTP. Some apps expect this to be 6 digits, others support more.
113 | :param digest: digest function to use in the HMAC (expected to be SHA1)
114 | :param name: account name
115 | :param issuer: issuer
116 | """
117 | if digest is None:
118 | digest = hashlib.sha1
119 |
120 | self.interval = interval
121 | super(TOTP, self).__init__(s=s, digits=digits, digest=digest, name=name, issuer=issuer)
122 |
123 | def at(self, for_time, counter_offset = 0):
124 | """
125 | Accepts either a Unix timestamp integer or a datetime object.
126 |
127 | To get the time until the next timecode change (seconds until the current OTP expires), use this instead:
128 |
129 | .. code:: python
130 |
131 | totp = pyotp.TOTP(...)
132 | time_remaining = totp.interval - datetime.datetime.now().timestamp() % totp.interval
133 |
134 | :param for_time: the time to generate an OTP for
135 | :param counter_offset: the amount of ticks to add to the time counter
136 | :returns: OTP value
137 | """
138 | if not isinstance(for_time, datetime.datetime):
139 | for_time = datetime.datetime.fromtimestamp(int(for_time))
140 | return self.generate_otp(self.timecode(for_time) + counter_offset)
141 |
142 | def now(self):
143 | """
144 | Generate the current time OTP
145 |
146 | :returns: OTP value
147 | """
148 | return self.generate_otp(self.timecode(datetime.datetime.now()))
149 |
150 | def verify(self, otp, for_time = None, valid_window = 0):
151 | """
152 | Verifies the OTP passed in against the current time OTP.
153 |
154 | :param otp: the OTP to check against
155 | :param for_time: Time to check OTP at (defaults to now)
156 | :param valid_window: extends the validity to this many counter ticks before and after the current one
157 | :returns: True if verification succeeded, False otherwise
158 | """
159 | if for_time is None:
160 | for_time = datetime.datetime.now()
161 |
162 | if valid_window:
163 | for i in range(-valid_window, valid_window + 1):
164 | if strings_equal(str(otp), str(self.at(for_time, i))):
165 | return True
166 | return False
167 |
168 | return strings_equal(str(otp), str(self.at(for_time)))
169 |
170 | def timecode(self, for_time):
171 | """
172 | Accepts either a timezone naive (`for_time.tzinfo is None`) or
173 | a timezone aware datetime as argument and returns the
174 | corresponding counter value (timecode).
175 |
176 | """
177 | if for_time.tzinfo:
178 | return int(calendar.timegm(for_time.utctimetuple()) / self.interval)
179 | else:
180 | return int(time.mktime(for_time.timetuple()) / self.interval)
181 |
182 | ####
183 | # END OF COPIED CODE
184 | # BIG DISCLAIMER
185 | # I only copied the relevant parts of https://pypi.org/project/pyotp/
186 | # The code there is MIT License (MIT License)
187 | ####
188 | #TODO: In practice it's always 6 digits, SHA-1, but it would be fairly easy to add some ifs and arguments to the TOTP() call if a TOTP would be different once
189 |
190 | # As an input use the secret from:
191 | #% zbarimg ~/Downloads/Authenticator-QR-Code.png
192 | #QR-Code:otpauth://totp/SomeOrg:something?secret=YESHOOLALALFOOOBARKOOKUCK&issuer=SomeOrg
193 | #scanned 1 barcode symbols from 1 images in 0.05 seconds
194 |
195 | totp = TOTP(input)
196 | output = str(totp.now())
197 | #input + strHello + str(intTest)
--------------------------------------------------------------------------------
/tag-store/unicode_overflow/unicode_overflow.js:
--------------------------------------------------------------------------------
1 | output = input.split('').map(chr =>
2 | String.fromCodePoint(mask + chr.codePointAt())
3 | ).join('');
--------------------------------------------------------------------------------
/tag-store/unicode_overflow_variations/unicode_overflow_variations.js:
--------------------------------------------------------------------------------
1 | if(max > 0xffff) {
2 | throw new Error("Max parameter is too large");
3 | }
4 | output = input.split('').map(chr => {
5 | let characters = '';
6 | for(let i=chr.codePointAt()+1;i<=max;i++){
7 | if(i % 256 === chr.codePointAt()) {
8 | characters += String.fromCodePoint(i);
9 | }
10 | }
11 | return characters;
12 | }).join('');
--------------------------------------------------------------------------------
/tag-store/uuid/uuid.py:
--------------------------------------------------------------------------------
1 | import uuid
2 |
3 | def generate_uuid(version):
4 | if version == "1":
5 | return uuid.uuid1()
6 | if version == "4":
7 | return uuid.uuid4()
8 | else:
9 | raise ValueError("Unsupported UUID version")
10 |
11 | output = str(generate_uuid(version))
--------------------------------------------------------------------------------