├── .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 | 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 | 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 | 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 | 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 | ![](https://github.com/hackvertor/hackvertor/blob/master/src/main/resources/images/logo-light.png) 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>` 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` 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 `` 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() + ""); 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() + "", hackvertor)); 24 | } else { 25 | outputArea.replaceSelection(hackvertor.convert("<@auto_decode_no_decrypt>" + inputArea.getSelectedText() + "", 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 ""; 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> "; 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"; 35 | assertEquals("61626364", hackvertor.convert(plaintext, hackvertor)); 36 | plaintext = "<@ascii2hex(' ')>abcd"; 37 | assertEquals("61 62 63 64", hackvertor.convert(plaintext, hackvertor)); 38 | plaintext = "<@ascii2hex(' ')>abcd"; 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> "; 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 -> 756.9217.0769.85 5 | # <@_ean13(0,'[...]')>756.9217.0769.8 -> 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)) --------------------------------------------------------------------------------