diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..c836cd2
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,51 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.0.0] - 2020-04-22
+
+### Added
+
+* Support operating on raw byte data.
+* Enable context menu inside the CSTC pane.
+* Add additional operations:
+ * *Divide* (Divide input by the given number).
+ * *Multiply* (Multiply input with the given number).
+ * *HttpCookieExtractor* (Extract cookies from *HTTP* requests).
+ * *HeaderSetter* (Set *HTTP* headers).
+ * *HttpSetBody* (Set *HTTP* body).
+ * *HttpSetCookie* (Set *HTTP* cookie).
+ * *HttpJsonSetter* (Set a JSON field in a HTTP request).
+ * *JsonSetter* (Set a value inside of a JSON string).
+ * *PostSetter* (Set a POST parameter).
+ * *XmlSetter* (Set a XML field in a HTTP request ).
+ * *HttpXmlExtractor* (Get a XML value from a HTTP request).
+ * *HttpJsonExtractor* (Get a JSON value from a HTTP request).
+* Add workflow demonstration in form of a GIF to README.md
+* Add a changelog :)
+
+### Changed
+
+* Fix typos in several modules.
+* Ignore the *IV* parameter when using encryption modules in *ECB* mode.
+* Support *raw* encoding for *FormattedTextFields*.
+* Make all operations work on raw bytes.
+* Implement the so far unimplemented input and output modes for encryption modules.
+* Correct syntax highlighting inside the CSTC pane.
+* Fix bugs in several different modules.
+* Update version of *jackson-databind*.
+* Adjust image icons displayed inside the nodetree.
+
+### Removed
+
+* Remove *FlowControl* and *Language* operation categories, as they are currently unused.
+* Remove *ReplaceBody* (was substituted by *HttpSetBody*).
+
+## [0.1.1] - 2019-08-20
+
+### Added
+
+* Initial release.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/README.md b/README.md
index 2578e32..5c08636 100644
--- a/README.md
+++ b/README.md
@@ -1,78 +1,87 @@
-Copyright 2017-2019 usd AG
+*Copyright 2017-2020 usd AG*
-Licensed under the GNU General Public License, Version 3.0 (the "License"); you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
+Licensed under the *GNU General Public License, Version 3.0* (the "License"). You may not use this tool except in compliance with the License.
+You may obtain a copy of the License at https://www.gnu.org/licenses/gpl-3.0.html
-https://www.gnu.org/licenses/gpl-3.0.html
+
-
+# Cyber Security Transformation Chef
+*The Cyber Security Transformation Chef* (*CSTC*) is a *Burp Suite* extension. It is build for security experts to
+extend *Burp Suite* for chaining simple operations on each incoming or outgoing *HTTP* message.
+It can also be used to quickly apply custom formatting on each message.
-# Cyber Security Transformation Chef
-The Cyber Security Transformation Chef (CSTC) is a Burp Suite extension. It is build for security experts to
-extend Burp Suite for chaining simple operations for each incomming or outgoing message.
-It can also be used to quickly make a special custom formatting for the message.
+
## Introduction
-[Burp Suite](https://portswigger.net/) is a general known tool which provides
-a wide area of tools and functionality for conducting a web application penetration
-test. One problem often encountered when using the Burp Suite for certain type of
-web applications is the lack of a quick extensibility or the capability
-of conducting basic operations on the messages.
-The Burp Suite provides some functionality which can be used to adapt to certain scenarios
-(i.e. the macro feature), however it is a time consuming process and error-prone.
+[Burp Suite](https://portswigger.net/) is a general known software which provides
+a wide area of tools and functionality for conducting web application penetration
+tests. One problem often encountered when using *Burp Suite* for certain types of
+web applications is the lack of quick extensibility or the capability
+of conducting basic operations on incoming or outgoing messages.
+*Burp Suite* provides some functionality which can be used to adapt to certain scenarios
+(i.e. the *macro feature*), however it is a time consuming process, difficult to learn and error-prone.
With the years we developed a software which provides a GUI which is adapted from the well known
-[CyberChef](https://gchq.github.io/CyberChef/) providing several small operations which can be chained
-to conduct complicated input transformations. There is no need of further coding. The extension eliminates
+[CyberChef](https://gchq.github.io/CyberChef/), providing several small operations which can be chained
+to conduct a complicated input transformation. The extension eliminates
the need of having several plugins for input and output transformations because it is build in a more generic way.
-The CSTC is especially useful for using the quite good capabilities of Burp Suite Professional (Burp Scanner, Backslash Powered Scanner, ...)
-on web applications using client side calculated MACs, sequence numbers, or similiar.
+*CSTC* is especially useful for using already existing capabilities of *Burp Suite Professional* (*Burp Scanner*, *Backslash Powered Scanner*, ...)
+on web applications using client side calculated *MACs*, sequence numbers, or similar protections for request validation.
+However, *CSTC* does also perfectly interoperate with other *Burp Suite* features that are available in the *Community Edition* (*Repeater*, *Intruder*, ...).
+
+It is also a great help for analyzing obfuscated *HTTP* based protocols because it can be used to de- and reobfuscate network traffic
+passing through the proxy. In this way, the analyst can concentrate on the task of finding vulnerabilities
+instead of writing a new extension for removing the obfuscation.
-It is also a great help at analyzing obfuscated HTTP based protocols because it can be used to de- and reobfuscate the traffic
-passing through the proxy. In this way, the analyst can concentrate on task of finding vulnerabilities
-instead of writing new extensions for removing the obfuscation.
+The plugin has been successfully tested and decreased the time for performing tedious input and output transformations on *HTTP* messages.
-The plugin has been succesfully tested and decreased the time for performing the right tasks and not
-"fighting with tool" to get what is needed to test.
+## Prerequisites
-## Prerequities
-The CSTC can be used with either Burp Suite Free and Burp Suite Profesionnal.
+*CSTC* can be used with either *Burp Suite Community Edition* or *Burp Suite Professional*.
## Installation
-The CSTC is currently not listed in the Burp Extension Storage, but will be added there as soon as PortSwigger acknolwedges the Extension.
+*CSTC* is currently not listed in the *Burp Extension Storage* (*BApp Store*), but will be added there as soon as *PortSwigger* acknowledges the extension.
We suggest to pull the source code and build it yourself, because you should never trust binaries
and should always review the code which is used in a productive setting.
-However, you can also pull a release from GitHub and install it by adding it the Burp Suite.
+However, you can also pull a release from *GitHub* and install it by adding it to *Burp Suite*.
### Build Process
-The build process is fairly easy. It currently requires a installed JDK and Maven to build.
-
-You can build the extension with the following commands
+The build process is fairly easy. It currently requires a installed *JDK* and *Maven* to build.
+You can build the extension with the following commands:
```
-git clone https://github.com/usdAG/cstc.git
-cd cstc
-mvn package
+$ git clone https://github.com/usdAG/cstc.git
+$ cd cstc
+$ mvn package
```
-Maven will automatically load the dependencies for building the extension and will build
-a jar containing all dependencies. The created Jar file CSTC-X.X.X-jar-with-dependencies in the target directory can be
-installed in Burp using the Extender->Add function.
+*Maven* will automatically load the dependencies for building the extension and will build
+a *Jar* containing all these dependencies. The created Jar file ``CSTC-X.X.X-jar-with-dependencies`` in the ``target`` directory can be
+installed in *Burp Suite* using the ``Extender->Add->Extensiontype-java`` feature.
## Usage
-The tool uses a GUI which basic idea similar to the CyberChef. However, it introduces
-a concept which we call "lane". The output of the transformation is always determined
-from the the last lane which has an active operation. Take a look at a basic tutorial
-[here](https://www.youtube.com/watch?v=BUXvWfb_YWU).
+
+The tool uses a GUI which basic idea is similar to the [CyberChef](https://gchq.github.io/CyberChef/). However, it introduces
+a new concept which we call *lanes*. The output of a *CSTC* transformation is always determined
+from the the last *lane* which has an active operation. This initially takes getting used to, but quickly feels intuitive.
+Take a look at our basic tutorial on [YouTube](https://www.youtube.com/watch?v=BUXvWfb_YWU) and make sure to read our initial
+*CSTC* [blog post](https://herolab.usd.de/news-cyber-security-transformation-chef/).
+
+
+## Known Issues
+
+Unfortunately, the GUI of some *CSTC Operations* does not really work well together with the **dark theme** of *Burp Suite*. Therefore,
+we recommend to use a **light theme** for the best user experience.
## Feedback
-We gladly appreciate all feedback, bug requests and feature requests.
+
+We gladly appreciate all feedback, bug reports and feature requests.
Please understand that this tool is under active development and therefore will
-probably contain some bugs.
+probably contain some bugs :)
diff --git a/example/example-server.py b/example/example-server.py
new file mode 100644
index 0000000..b9fe54a
--- /dev/null
+++ b/example/example-server.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python3
+
+import logging
+import gzip
+import base64
+from sys import argv
+from http.server import BaseHTTPRequestHandler, HTTPServer
+
+class S(BaseHTTPRequestHandler):
+
+ def _set_response(self):
+ self.send_response(200)
+ self.send_header('Content-type', 'text/html')
+ self.end_headers()
+
+ def do_GET(self):
+ logging.info("GET request,\nPath: %s\nHeaders:\n%s\n", str(self.path), str(self.headers))
+ self._set_response()
+ self.wfile.write("GET request for {}".format(self.path).encode('utf-8'))
+
+ def do_POST(self):
+ content_length = int(self.headers['Content-Length'])
+ post_data = self.rfile.read(content_length)
+
+ try:
+ result = base64.b64decode(post_data)
+ except:
+ self._set_response()
+ self.wfile.write(b"Error 1021: Server expects Base64 encoded and gzip compressed data.")
+ return
+
+ try:
+ result = gzip.decompress(result)
+ except:
+ self._set_response()
+ self.wfile.write(b"Error 1022: Server expects Base64 encoded and gzip compressed data.")
+ return
+
+ self._set_response()
+ self.wfile.write(b"Processing Input: '" + result + b"'...
")
+
+
+def run(server_class=HTTPServer, handler_class=S, port=8080):
+ logging.basicConfig(level=logging.INFO)
+ server_address = ('', port)
+ httpd = server_class(server_address, handler_class)
+ logging.info('Starting CSTC Example Server.\n')
+ try:
+ httpd.serve_forever()
+ except KeyboardInterrupt:
+ pass
+ httpd.server_close()
+ logging.info('Stopping CSTC Example Server...\n')
+
+
+if __name__ == '__main__':
+ if len(argv) == 2:
+ run(port=int(argv[1]))
+ else:
+ run()
diff --git a/logos/CSTC_White_Smaller.png b/logos/CSTC_White_Smaller.png
deleted file mode 100644
index 82c2c88..0000000
Binary files a/logos/CSTC_White_Smaller.png and /dev/null differ
diff --git a/media/CSTC_White_Smaller.png b/media/CSTC_White_Smaller.png
new file mode 100644
index 0000000..f8377a0
Binary files /dev/null and b/media/CSTC_White_Smaller.png differ
diff --git a/media/CSTC_Workflow.gif b/media/CSTC_Workflow.gif
new file mode 100644
index 0000000..08c87ac
Binary files /dev/null and b/media/CSTC_Workflow.gif differ
diff --git a/pom.xml b/pom.xml
index e64a5aa..50111cc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2,7 +2,7 @@
4.0.0
de.usd.CSTC
CSTC
- 0.1.1
+ 1.0.0
CSTC
CSTC
@@ -74,7 +74,7 @@
com.fasterxml.jackson.core
jackson-databind
- 2.9.9.2
+ 2.9.10.3
diff --git a/res/folder_closed.png b/res/folder_closed.png
new file mode 100644
index 0000000..ebc2f0b
Binary files /dev/null and b/res/folder_closed.png differ
diff --git a/res/folder_open.png b/res/folder_open.png
new file mode 100644
index 0000000..b5ae647
Binary files /dev/null and b/res/folder_open.png differ
diff --git a/res/operation.png b/res/operation.png
new file mode 100644
index 0000000..c1103f7
Binary files /dev/null and b/res/operation.png differ
diff --git a/src/burp/BurpExtender.java b/src/burp/BurpExtender.java
index 1880b88..c5dbbe3 100644
--- a/src/burp/BurpExtender.java
+++ b/src/burp/BurpExtender.java
@@ -1,16 +1,12 @@
package burp;
import java.awt.Component;
-import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
+
import javax.swing.JMenuItem;
-import javax.swing.JTextArea;
-import javax.swing.JTextField;
-import javax.swing.plaf.basic.BasicTabbedPaneUI;
-import javax.swing.text.JTextComponent;
import de.usd.cstchef.view.FormatTab;
import de.usd.cstchef.view.RecipePanel;
@@ -79,7 +75,7 @@ public List createMenuItems(IContextMenuInvocation invoc) {
public void actionPerformed(ActionEvent e) {
IHttpRequestResponse[] msgs = invoc.getSelectedMessages();
if (msgs != null && msgs.length > 0) {
- view.getIncomingRecipePanel().setInput(new String(msgs[0].getResponse()));
+ view.getIncomingRecipePanel().setInput(msgs[0]);
}
}
});
@@ -89,7 +85,7 @@ public void actionPerformed(ActionEvent e) {
public void actionPerformed(ActionEvent e) {
IHttpRequestResponse[] msgs = invoc.getSelectedMessages();
if (msgs != null && msgs.length > 0) {
- view.getOutgoingRecipePanel().setInput(new String(msgs[0].getRequest()));
+ view.getOutgoingRecipePanel().setInput(msgs[0]);
}
}
@@ -100,7 +96,7 @@ public void actionPerformed(ActionEvent e) {
public void actionPerformed(ActionEvent e) {
IHttpRequestResponse[] msgs = invoc.getSelectedMessages();
if (msgs != null && msgs.length > 0) {
- view.getFormatRecipePanel().setInput(new String(msgs[0].getRequest()));
+ view.getFormatRecipePanel().setInput(msgs[0]);
}
}
});
@@ -115,4 +111,4 @@ public IMessageEditorTab createNewInstance(IMessageEditorController controller,
RecipePanel responseFormatPanel = this.view.getFormatRecipePanel();
return new FormatTab(requestFormatPanel, responseFormatPanel, editable);
}
-}
\ No newline at end of file
+}
diff --git a/src/burp/CstcMessageEditorController.java b/src/burp/CstcMessageEditorController.java
new file mode 100644
index 0000000..b25c775
--- /dev/null
+++ b/src/burp/CstcMessageEditorController.java
@@ -0,0 +1,37 @@
+package burp;
+
+public class CstcMessageEditorController implements IMessageEditorController {
+
+ private IHttpService httpService = null;
+ private byte[] request = null;
+ private byte[] response = null;
+
+ public void setHttpRequestResponse(IHttpRequestResponse requestResponse) {
+ this.httpService = requestResponse.getHttpService();
+ this.request = requestResponse.getRequest();
+ this.response = requestResponse.getResponse();
+ }
+
+ public void setRequest(byte[] request) {
+ this.request = request;
+ }
+
+ public void setResponse(byte[] response) {
+ this.request = response;
+ }
+
+ @Override
+ public IHttpService getHttpService() {
+ return httpService;
+ }
+
+ @Override
+ public byte[] getRequest() {
+ return request;
+ }
+
+ @Override
+ public byte[] getResponse() {
+ return response;
+ }
+}
diff --git a/src/de/usd/cstchef/Utils.java b/src/de/usd/cstchef/Utils.java
index 9d34fb3..54047e3 100644
--- a/src/de/usd/cstchef/Utils.java
+++ b/src/de/usd/cstchef/Utils.java
@@ -6,6 +6,7 @@
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
@@ -16,9 +17,11 @@
import burp.Logger;
import de.usd.cstchef.operations.Operation;
import de.usd.cstchef.operations.arithmetic.Addition;
+import de.usd.cstchef.operations.arithmetic.Divide;
import de.usd.cstchef.operations.arithmetic.DivideList;
import de.usd.cstchef.operations.arithmetic.Mean;
import de.usd.cstchef.operations.arithmetic.Median;
+import de.usd.cstchef.operations.arithmetic.Multiply;
import de.usd.cstchef.operations.arithmetic.MultiplyList;
import de.usd.cstchef.operations.arithmetic.Subtraction;
import de.usd.cstchef.operations.arithmetic.Sum;
@@ -38,16 +41,19 @@
import de.usd.cstchef.operations.dataformat.UrlEncode;
import de.usd.cstchef.operations.datetime.DateTime;
import de.usd.cstchef.operations.datetime.UnixTimestamp;
-import de.usd.cstchef.operations.encrpytion.AesDecryption;
-import de.usd.cstchef.operations.encrpytion.AesEncryption;
-import de.usd.cstchef.operations.encrpytion.DesDecryption;
-import de.usd.cstchef.operations.encrpytion.DesEncryption;
+import de.usd.cstchef.operations.encryption.AesDecryption;
+import de.usd.cstchef.operations.encryption.AesEncryption;
+import de.usd.cstchef.operations.encryption.DesDecryption;
+import de.usd.cstchef.operations.encryption.DesEncryption;
import de.usd.cstchef.operations.extractors.HttpBodyExtractor;
+import de.usd.cstchef.operations.extractors.HttpCookieExtractor;
import de.usd.cstchef.operations.extractors.HttpGetExtractor;
import de.usd.cstchef.operations.extractors.HttpHeaderExtractor;
+import de.usd.cstchef.operations.extractors.HttpJsonExtractor;
import de.usd.cstchef.operations.extractors.HttpMethodExtractor;
import de.usd.cstchef.operations.extractors.HttpPostExtractor;
import de.usd.cstchef.operations.extractors.HttpUriExtractor;
+import de.usd.cstchef.operations.extractors.HttpXmlExtractor;
import de.usd.cstchef.operations.extractors.JsonExtractor;
import de.usd.cstchef.operations.extractors.RegexExtractor;
import de.usd.cstchef.operations.hashing.Blake;
@@ -67,14 +73,20 @@
import de.usd.cstchef.operations.misc.ReadFile;
import de.usd.cstchef.operations.misc.WriteFile;
import de.usd.cstchef.operations.networking.HTTPRequest;
-import de.usd.cstchef.operations.setter.GetSetter;
+import de.usd.cstchef.operations.setter.HttpGetSetter;
+import de.usd.cstchef.operations.setter.HttpHeaderSetter;
+import de.usd.cstchef.operations.setter.HttpJsonSetter;
+import de.usd.cstchef.operations.setter.HttpPostSetter;
+import de.usd.cstchef.operations.setter.HttpSetBody;
+import de.usd.cstchef.operations.setter.HttpSetCookie;
import de.usd.cstchef.operations.setter.HttpSetUri;
+import de.usd.cstchef.operations.setter.HttpXmlSetter;
+import de.usd.cstchef.operations.setter.JsonSetter;
import de.usd.cstchef.operations.signature.XmlFullSignature;
import de.usd.cstchef.operations.signature.XmlMultiSignature;
import de.usd.cstchef.operations.string.Length;
import de.usd.cstchef.operations.string.Prefix;
import de.usd.cstchef.operations.string.Replace;
-import de.usd.cstchef.operations.string.ReplaceBody;
import de.usd.cstchef.operations.string.StaticString;
import de.usd.cstchef.operations.string.Substring;
import de.usd.cstchef.operations.string.Suffix;
@@ -113,6 +125,21 @@ public static String replaceVariables(String text) {
return text;
}
+ public static byte[] replaceVariablesByte(byte[] bytes) {
+ HashMap variables = VariableStore.getInstance().getVariables();
+
+ byte[] currentKey;
+ for (Entry entry : variables.entrySet()) {
+
+ currentKey = ("ยง" + entry.getKey()).getBytes();
+ if( Arrays.equals(currentKey, bytes) ) {
+ bytes = entry.getValue();
+ }
+
+ }
+ return bytes;
+ }
+
public static Class extends Operation>[] getOperationsBurp() {
ZipInputStream zip = null;
List> operations = new ArrayList>();
@@ -154,18 +181,23 @@ public static Class extends Operation>[] getOperationsBurp() {
// TODO reflection does not work in Burp Suite
@SuppressWarnings("unchecked")
public static Class extends Operation>[] getOperationsDev() {
- return new Class[] { RegexExtractor.class, WriteFile.class, ReadFile.class, Length.class, UrlDecode.class, UrlEncode.class,
- HTTPRequest.class, SHA1.class, Hmac.class, Gost.class, Median.class, RIPEMD.class, DesDecryption.class,
- MultiplyList.class, Skein.class, StoreVariable.class, JsonExtractor.class, Sum.class, GetVariable.class,
- HttpUriExtractor.class, HttpBodyExtractor.class, HttpPostExtractor.class,
- HttpGetExtractor.class, Sub.class, Replace.class, DivideList.class, ToHex.class, FromHex.class, MD5.class,
- AesDecryption.class, Suffix.class, SHA2.class, Prefix.class, MD4.class, Whirlpool.class,
- StaticString.class, AddKey.class, FromBase64.class, DSTU7564.class, Substring.class, ToBase64.class,
- SHA3.class, HttpMethodExtractor.class, MD2.class, Blake.class, AesEncryption.class,
- Tiger.class, DesEncryption.class, HttpHeaderExtractor.class, And.class, Mean.class,
- XmlFullSignature.class, XmlMultiSignature.class, ReplaceBody.class,
- DateTime.class, Addition.class, Subtraction.class, GetSetter.class,
- Deflate.class, Inflate.class, Gzip.class, GUnzip.class, UnixTimestamp.class, Xor.class, HttpSetUri.class };
+ return new Class[] {
+ Addition.class, AddKey.class, AesDecryption.class, AesEncryption.class, And.class,
+ Blake.class, DateTime.class, Deflate.class, DesDecryption.class, DesEncryption.class,
+ Divide.class, DivideList.class, DSTU7564.class, FromBase64.class, FromHex.class,
+ GetVariable.class, Gost.class, GUnzip.class, Gzip.class, Hmac.class,
+ HttpBodyExtractor.class, HttpCookieExtractor.class, HttpGetExtractor.class, HttpGetSetter.class, HttpHeaderExtractor.class,
+ HttpHeaderSetter.class, HttpJsonExtractor.class, HttpJsonSetter.class, HttpMethodExtractor.class, HttpPostExtractor.class,
+ HttpPostSetter.class, HTTPRequest.class, HttpSetBody.class, HttpSetCookie.class, HttpSetUri.class,
+ HttpUriExtractor.class, HttpXmlExtractor.class, HttpXmlSetter.class, Inflate.class, JsonExtractor.class,
+ JsonSetter.class, Length.class, MD2.class, MD4.class, MD5.class,
+ Mean.class, Median.class, Multiply.class, MultiplyList.class, Prefix.class,
+ ReadFile.class, RegexExtractor.class, Replace.class, RIPEMD.class, SHA1.class,
+ SHA2.class, SHA3.class, Skein.class, StaticString.class, StoreVariable.class,
+ Sub.class, Substring.class, Subtraction.class, Suffix.class, Sum.class,
+ Tiger.class, ToBase64.class, ToHex.class, UnixTimestamp.class, UrlDecode.class,
+ UrlEncode.class, Whirlpool.class, WriteFile.class, XmlFullSignature.class, XmlMultiSignature.class,
+ Xor.class };
}
public static Class extends Operation>[] getOperations() {
diff --git a/src/de/usd/cstchef/operations/OperationCategory.java b/src/de/usd/cstchef/operations/OperationCategory.java
index 8d48a6b..e9e87b9 100644
--- a/src/de/usd/cstchef/operations/OperationCategory.java
+++ b/src/de/usd/cstchef/operations/OperationCategory.java
@@ -1,22 +1,22 @@
package de.usd.cstchef.operations;
public enum OperationCategory {
+ ARITHMETIC("Arithmetic"),
+ BYTEOPERATION("Byte Operations"),
+ COMPRESSION("Compression"),
DATAFORMAT("Data format"),
+ DATES("Date / Time"),
+ ENCRYPTION("Encryption / Encoding"),
EXTRACTORS("Extractors"),
- SETTER("Setter"),
- STRING("String"),
HASHING("Hashing"),
- BYTEOPERATION("Byte Operations"),
- ARITHMETIC("Arithmetic"),
+ MISC("Misc"),
NETWORKING("Networking"),
- UTILS("Utils"),
- DATES("Date / Time"),
- ENCRYPTION("Encryption / Encoding"),
+ SETTER("Setter"),
SIGNATURE("Signature"),
- MISC("Misc"),
- COMPRESSION("Compression"),
- LANGUAGE("Language"),
- FLOWCONTROL("Flow control");
+ STRING("String"),
+ UTILS("Utils");
+// LANGUAGE("Language"),
+// FLOWCONTROL("Flow control");
private final String text;
@@ -28,5 +28,4 @@ public enum OperationCategory {
public String toString() {
return text;
}
-
}
diff --git a/src/de/usd/cstchef/operations/arithmetic/Addition.java b/src/de/usd/cstchef/operations/arithmetic/Addition.java
index cfa1d14..4daacd0 100644
--- a/src/de/usd/cstchef/operations/arithmetic/Addition.java
+++ b/src/de/usd/cstchef/operations/arithmetic/Addition.java
@@ -3,7 +3,7 @@
import de.usd.cstchef.operations.Operation.OperationInfos;
import de.usd.cstchef.operations.OperationCategory;
-@OperationInfos(name = "Add", category = OperationCategory.ARITHMETIC, description = "Adds to the input the given number.")
+@OperationInfos(name = "Single - Add", category = OperationCategory.ARITHMETIC, description = "Add to the input the given number.")
public class Addition extends ArithmeticOperation {
@Override
diff --git a/src/de/usd/cstchef/operations/arithmetic/ArithmeticOperation.java b/src/de/usd/cstchef/operations/arithmetic/ArithmeticOperation.java
index 30a48ec..02b11ff 100644
--- a/src/de/usd/cstchef/operations/arithmetic/ArithmeticOperation.java
+++ b/src/de/usd/cstchef/operations/arithmetic/ArithmeticOperation.java
@@ -12,26 +12,33 @@ public abstract class ArithmeticOperation extends Operation {
@Override
protected byte[] perform(byte[] input) throws Exception {
- String i = new String(input);
-
- //A little trick
- if (i.isEmpty()) {
- i = "0";
- }
-
- Double input_number = Double.valueOf(i);
- Double static_number = Double.valueOf(numberInput.getText());
- Double result_number = calculate(input_number, static_number);
String result = "";
- if (this.floatCheckBox.isSelected()) {
- result = String.valueOf(result_number);
- }
- else {
- result = String.valueOf(Math.round(result_number));
+ try {
+
+ String i = new String(input);
+
+ //A little trick
+ if (i.isEmpty()) {
+ i = "0";
+ }
+
+ Double input_number = Double.valueOf(i);
+ Double static_number = Double.valueOf(numberInput.getText());
+ Double result_number = calculate(input_number, static_number);
+
+
+ if (this.floatCheckBox.isSelected()) {
+ result = String.valueOf(result_number);
+ } else {
+ result = String.valueOf(Math.round(result_number));
+ }
+
+ } catch( Exception e ) {
+ throw new IllegalArgumentException("Input is not a number.");
}
- return result.getBytes();
+ return result.getBytes();
}
protected abstract double calculate(double input_number, double static_number);
diff --git a/src/de/usd/cstchef/operations/arithmetic/Divide.java b/src/de/usd/cstchef/operations/arithmetic/Divide.java
new file mode 100644
index 0000000..5c2a9d3
--- /dev/null
+++ b/src/de/usd/cstchef/operations/arithmetic/Divide.java
@@ -0,0 +1,40 @@
+package de.usd.cstchef.operations.arithmetic;
+
+import javax.swing.JCheckBox;
+
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "Single - Divide", category = OperationCategory.ARITHMETIC, description = "Divide input by the given number")
+public class Divide extends ArithmeticOperation {
+
+ private JCheckBox reverse;
+
+ @Override
+ protected double calculate(double input_number, double static_number) {
+
+ if( reverse.isSelected() ) {
+
+ if( input_number == 0 )
+ input_number = 1;
+
+ return static_number / input_number;
+
+ } else {
+
+ if( static_number == 0 )
+ static_number = 1;
+
+ return input_number / static_number;
+
+ }
+ }
+
+ @Override
+ public void createUI() {
+ super.createUI();
+
+ this.reverse = new JCheckBox();
+ this.addUIElement("Reverse", this.reverse);
+ }
+}
diff --git a/src/de/usd/cstchef/operations/arithmetic/DivideList.java b/src/de/usd/cstchef/operations/arithmetic/DivideList.java
index 0171270..bc4992c 100644
--- a/src/de/usd/cstchef/operations/arithmetic/DivideList.java
+++ b/src/de/usd/cstchef/operations/arithmetic/DivideList.java
@@ -3,7 +3,7 @@
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
-@OperationInfos(name = "Divide", category = OperationCategory.ARITHMETIC, description = "Divides a list of numbers.")
+@OperationInfos(name = "List - Divide", category = OperationCategory.ARITHMETIC, description = "Divides a list of numbers.")
public class DivideList extends ArithmeticDelimiterOperation {
@Override
diff --git a/src/de/usd/cstchef/operations/arithmetic/Mean.java b/src/de/usd/cstchef/operations/arithmetic/Mean.java
index e28bf20..c0b96f1 100644
--- a/src/de/usd/cstchef/operations/arithmetic/Mean.java
+++ b/src/de/usd/cstchef/operations/arithmetic/Mean.java
@@ -3,7 +3,7 @@
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
-@OperationInfos(name = "Mean", category = OperationCategory.ARITHMETIC, description = "Computes the mean of a list of numbers.")
+@OperationInfos(name = "List - Mean", category = OperationCategory.ARITHMETIC, description = "Computes the mean of a list of numbers.")
public class Mean extends ArithmeticDelimiterOperation {
@Override
diff --git a/src/de/usd/cstchef/operations/arithmetic/Median.java b/src/de/usd/cstchef/operations/arithmetic/Median.java
index 429f7b5..dad3f38 100644
--- a/src/de/usd/cstchef/operations/arithmetic/Median.java
+++ b/src/de/usd/cstchef/operations/arithmetic/Median.java
@@ -5,7 +5,7 @@
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
-@OperationInfos(name = "Median", category = OperationCategory.ARITHMETIC, description = "Computes the median of a list of numbers.")
+@OperationInfos(name = "List - Median", category = OperationCategory.ARITHMETIC, description = "Computes the median of a list of numbers.")
public class Median extends ArithmeticDelimiterOperation {
@Override
diff --git a/src/de/usd/cstchef/operations/arithmetic/Multiply.java b/src/de/usd/cstchef/operations/arithmetic/Multiply.java
new file mode 100644
index 0000000..0b9b5a9
--- /dev/null
+++ b/src/de/usd/cstchef/operations/arithmetic/Multiply.java
@@ -0,0 +1,14 @@
+package de.usd.cstchef.operations.arithmetic;
+
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "Single - Multiply", category = OperationCategory.ARITHMETIC, description = "Multiply input with the given number")
+public class Multiply extends ArithmeticOperation {
+
+ @Override
+ protected double calculate(double input_number, double static_number) {
+ return input_number * static_number;
+ }
+
+}
diff --git a/src/de/usd/cstchef/operations/arithmetic/MultiplyList.java b/src/de/usd/cstchef/operations/arithmetic/MultiplyList.java
index eb6b72d..2ba1e6e 100644
--- a/src/de/usd/cstchef/operations/arithmetic/MultiplyList.java
+++ b/src/de/usd/cstchef/operations/arithmetic/MultiplyList.java
@@ -3,7 +3,7 @@
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
-@OperationInfos(name = "Multiply", category = OperationCategory.ARITHMETIC, description = "Multiplies a list of numbers.")
+@OperationInfos(name = "List - Multiply", category = OperationCategory.ARITHMETIC, description = "Multiplies a list of numbers.")
public class MultiplyList extends ArithmeticDelimiterOperation {
@Override
diff --git a/src/de/usd/cstchef/operations/arithmetic/Subtraction.java b/src/de/usd/cstchef/operations/arithmetic/Subtraction.java
index 76a500d..63e6c34 100644
--- a/src/de/usd/cstchef/operations/arithmetic/Subtraction.java
+++ b/src/de/usd/cstchef/operations/arithmetic/Subtraction.java
@@ -3,7 +3,7 @@
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
-@OperationInfos(name = "Subtract", category = OperationCategory.ARITHMETIC, description = "Subtracts from the input the given number.")
+@OperationInfos(name = "Single - Subtract", category = OperationCategory.ARITHMETIC, description = "Subtract from the input the given number.")
public class Subtraction extends ArithmeticOperation {
@Override
diff --git a/src/de/usd/cstchef/operations/arithmetic/Sum.java b/src/de/usd/cstchef/operations/arithmetic/Sum.java
index 928d5cb..9f6256d 100644
--- a/src/de/usd/cstchef/operations/arithmetic/Sum.java
+++ b/src/de/usd/cstchef/operations/arithmetic/Sum.java
@@ -3,7 +3,7 @@
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
-@OperationInfos(name = "Sum", category = OperationCategory.ARITHMETIC, description = "Sums a list of numbers.")
+@OperationInfos(name = "List - Sum", category = OperationCategory.ARITHMETIC, description = "Sums a list of numbers.")
public class Sum extends ArithmeticDelimiterOperation {
@Override
diff --git a/src/de/usd/cstchef/operations/dataformat/FromHex.java b/src/de/usd/cstchef/operations/dataformat/FromHex.java
index dec29f3..3ba4659 100644
--- a/src/de/usd/cstchef/operations/dataformat/FromHex.java
+++ b/src/de/usd/cstchef/operations/dataformat/FromHex.java
@@ -26,7 +26,7 @@ protected byte[] perform(byte[] input) throws Exception {
String delimiterStr = new String(delimiter.value);
String inputStr = new String(input);
- inputStr = inputStr.replaceAll(delimiterStr, "");
+ inputStr = inputStr.replace(delimiterStr, "");
return Hex.decode(inputStr);
}
diff --git a/src/de/usd/cstchef/operations/dataformat/UrlDecode.java b/src/de/usd/cstchef/operations/dataformat/UrlDecode.java
index 1fab0da..0a04ff1 100644
--- a/src/de/usd/cstchef/operations/dataformat/UrlDecode.java
+++ b/src/de/usd/cstchef/operations/dataformat/UrlDecode.java
@@ -1,18 +1,22 @@
package de.usd.cstchef.operations.dataformat;
-import java.net.URLDecoder;
-
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
import de.usd.cstchef.operations.Operation;
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
-@OperationInfos(name = "Url decode", category = OperationCategory.DATAFORMAT, description = "Url decoding")
+@OperationInfos(name = "Url Decode", category = OperationCategory.DATAFORMAT, description = "Url decoding")
public class UrlDecode extends Operation {
@Override
protected byte[] perform(byte[] input) throws Exception {
- String result = URLDecoder.decode(new String(input), "UTF-8");
- return result.getBytes();
+ IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = cbs.getHelpers();
+
+ byte[] result = helpers.urlDecode(input);
+ return result;
}
}
diff --git a/src/de/usd/cstchef/operations/dataformat/UrlEncode.java b/src/de/usd/cstchef/operations/dataformat/UrlEncode.java
index db729ee..799f5dd 100644
--- a/src/de/usd/cstchef/operations/dataformat/UrlEncode.java
+++ b/src/de/usd/cstchef/operations/dataformat/UrlEncode.java
@@ -1,18 +1,54 @@
package de.usd.cstchef.operations.dataformat;
-import java.net.URLEncoder;
+import java.io.ByteArrayOutputStream;
+import javax.swing.JCheckBox;
+
+import org.bouncycastle.util.encoders.Hex;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
import de.usd.cstchef.operations.Operation;
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
-@OperationInfos(name = "Url encode", category = OperationCategory.DATAFORMAT, description = "Url encode")
+@OperationInfos(name = "Url Encode", category = OperationCategory.DATAFORMAT, description = "Url encode")
public class UrlEncode extends Operation {
+ private JCheckBox checkbox;
+
@Override
protected byte[] perform(byte[] input) throws Exception {
- String result = URLEncoder.encode(new String(input), "UTF-8");
- return result.getBytes();
+ IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = cbs.getHelpers();
+
+ byte[] result = null;
+ if( checkbox.isSelected() ) {
+
+ byte[] delimiter = "%".getBytes();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(delimiter);
+
+ for (int i = 0; i < input.length - 1; i++) {
+ out.write(Hex.encode(new byte[] { input[i] }));
+ out.write(delimiter);
+ }
+
+ out.write(Hex.encode(new byte[] { input[input.length - 1] }));
+ result = out.toByteArray();
+
+ } else {
+ result = helpers.urlEncode(input);
+ }
+
+ return result;
}
+ @Override
+ public void createUI() {
+ this.checkbox = new JCheckBox("Encode all");
+ this.checkbox.setSelected(false);
+ this.addUIElement(null, this.checkbox);
+ }
}
diff --git a/src/de/usd/cstchef/operations/encrpytion/AesDecryption.java b/src/de/usd/cstchef/operations/encryption/AesDecryption.java
similarity index 87%
rename from src/de/usd/cstchef/operations/encrpytion/AesDecryption.java
rename to src/de/usd/cstchef/operations/encryption/AesDecryption.java
index 5279167..0e5abc6 100644
--- a/src/de/usd/cstchef/operations/encrpytion/AesDecryption.java
+++ b/src/de/usd/cstchef/operations/encryption/AesDecryption.java
@@ -1,4 +1,4 @@
-package de.usd.cstchef.operations.encrpytion;
+package de.usd.cstchef.operations.encryption;
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
@@ -10,4 +10,4 @@ public AesDecryption() {
super("AES");
}
-}
\ No newline at end of file
+}
diff --git a/src/de/usd/cstchef/operations/encrpytion/AesEncryption.java b/src/de/usd/cstchef/operations/encryption/AesEncryption.java
similarity index 88%
rename from src/de/usd/cstchef/operations/encrpytion/AesEncryption.java
rename to src/de/usd/cstchef/operations/encryption/AesEncryption.java
index 13d02e7..8d3a02f 100644
--- a/src/de/usd/cstchef/operations/encrpytion/AesEncryption.java
+++ b/src/de/usd/cstchef/operations/encryption/AesEncryption.java
@@ -1,4 +1,4 @@
-package de.usd.cstchef.operations.encrpytion;
+package de.usd.cstchef.operations.encryption;
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
diff --git a/src/de/usd/cstchef/operations/encrpytion/CipherUtils.java b/src/de/usd/cstchef/operations/encryption/CipherUtils.java
similarity index 97%
rename from src/de/usd/cstchef/operations/encrpytion/CipherUtils.java
rename to src/de/usd/cstchef/operations/encryption/CipherUtils.java
index 662835d..117c138 100644
--- a/src/de/usd/cstchef/operations/encrpytion/CipherUtils.java
+++ b/src/de/usd/cstchef/operations/encryption/CipherUtils.java
@@ -1,4 +1,4 @@
-package de.usd.cstchef.operations.encrpytion;
+package de.usd.cstchef.operations.encryption;
import java.security.Provider;
import java.security.Security;
diff --git a/src/de/usd/cstchef/operations/encrpytion/CryptOperation.java b/src/de/usd/cstchef/operations/encryption/CryptOperation.java
similarity index 68%
rename from src/de/usd/cstchef/operations/encrpytion/CryptOperation.java
rename to src/de/usd/cstchef/operations/encryption/CryptOperation.java
index 35a410c..a0ca5f2 100644
--- a/src/de/usd/cstchef/operations/encrpytion/CryptOperation.java
+++ b/src/de/usd/cstchef/operations/encryption/CryptOperation.java
@@ -1,17 +1,20 @@
-package de.usd.cstchef.operations.encrpytion;
+package de.usd.cstchef.operations.encryption;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.swing.JComboBox;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.encoders.Base64;
+
import de.usd.cstchef.operations.Operation;
-import de.usd.cstchef.operations.encrpytion.CipherUtils.CipherInfo;
+import de.usd.cstchef.operations.encryption.CipherUtils.CipherInfo;
import de.usd.cstchef.view.ui.FormatTextField;
public abstract class CryptOperation extends Operation {
- private static String[] inOutModes = new String[] { "Raw", "Hex" };
+ private static String[] inOutModes = new String[] { "Raw", "Hex", "Base64" };
protected String algorithm;
protected FormatTextField ivTxt;
@@ -36,8 +39,27 @@ protected byte[] crypt(byte[] input, int cipherMode, String algorithm, String mo
SecretKeySpec secretKeySpec = new SecretKeySpec(key, algorithm);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(String.format("%s/%s/%s", algorithm, mode, padding));
- cipher.init(cipherMode, secretKeySpec, ivSpec);
+
+ if( mode.equals("ECB") ) {
+ cipher.init(cipherMode, secretKeySpec);
+ } else {
+ cipher.init(cipherMode, secretKeySpec, ivSpec);
+ }
+
+ String selectedInputMode = (String)inputMode.getSelectedItem();
+ String selectedOutputMode = (String)outputMode.getSelectedItem();
+
+ if( selectedInputMode.equals("Hex") )
+ input = Hex.decode(input);
+ if( selectedInputMode.equals("Base64") )
+ input = Base64.decode(input);
+
byte[] encrypted = cipher.doFinal(input);
+
+ if( selectedOutputMode.equals("Hex") )
+ encrypted = Hex.encode(encrypted);
+ if( selectedOutputMode.equals("Base64") )
+ encrypted = Base64.encode(encrypted);
return encrypted;
}
diff --git a/src/de/usd/cstchef/operations/encrpytion/DecryptionOperation.java b/src/de/usd/cstchef/operations/encryption/DecryptionOperation.java
similarity index 89%
rename from src/de/usd/cstchef/operations/encrpytion/DecryptionOperation.java
rename to src/de/usd/cstchef/operations/encryption/DecryptionOperation.java
index 41990f6..9d7b7b6 100644
--- a/src/de/usd/cstchef/operations/encrpytion/DecryptionOperation.java
+++ b/src/de/usd/cstchef/operations/encryption/DecryptionOperation.java
@@ -1,4 +1,4 @@
-package de.usd.cstchef.operations.encrpytion;
+package de.usd.cstchef.operations.encryption;
import javax.crypto.Cipher;
diff --git a/src/de/usd/cstchef/operations/encrpytion/DesDecryption.java b/src/de/usd/cstchef/operations/encryption/DesDecryption.java
similarity index 87%
rename from src/de/usd/cstchef/operations/encrpytion/DesDecryption.java
rename to src/de/usd/cstchef/operations/encryption/DesDecryption.java
index 67aba38..6a4253e 100644
--- a/src/de/usd/cstchef/operations/encrpytion/DesDecryption.java
+++ b/src/de/usd/cstchef/operations/encryption/DesDecryption.java
@@ -1,4 +1,4 @@
-package de.usd.cstchef.operations.encrpytion;
+package de.usd.cstchef.operations.encryption;
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
@@ -10,4 +10,4 @@ public DesDecryption() {
super("DES");
}
-}
\ No newline at end of file
+}
diff --git a/src/de/usd/cstchef/operations/encrpytion/DesEncryption.java b/src/de/usd/cstchef/operations/encryption/DesEncryption.java
similarity index 88%
rename from src/de/usd/cstchef/operations/encrpytion/DesEncryption.java
rename to src/de/usd/cstchef/operations/encryption/DesEncryption.java
index 5e6aca3..032298e 100644
--- a/src/de/usd/cstchef/operations/encrpytion/DesEncryption.java
+++ b/src/de/usd/cstchef/operations/encryption/DesEncryption.java
@@ -1,4 +1,4 @@
-package de.usd.cstchef.operations.encrpytion;
+package de.usd.cstchef.operations.encryption;
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
diff --git a/src/de/usd/cstchef/operations/encrpytion/EncryptionOperation.java b/src/de/usd/cstchef/operations/encryption/EncryptionOperation.java
similarity index 89%
rename from src/de/usd/cstchef/operations/encrpytion/EncryptionOperation.java
rename to src/de/usd/cstchef/operations/encryption/EncryptionOperation.java
index 7f74eb7..65e90ed 100644
--- a/src/de/usd/cstchef/operations/encrpytion/EncryptionOperation.java
+++ b/src/de/usd/cstchef/operations/encryption/EncryptionOperation.java
@@ -1,4 +1,4 @@
-package de.usd.cstchef.operations.encrpytion;
+package de.usd.cstchef.operations.encryption;
import javax.crypto.Cipher;
diff --git a/src/de/usd/cstchef/operations/extractors/HttpCookieExtractor.java b/src/de/usd/cstchef/operations/extractors/HttpCookieExtractor.java
new file mode 100644
index 0000000..a1616d8
--- /dev/null
+++ b/src/de/usd/cstchef/operations/extractors/HttpCookieExtractor.java
@@ -0,0 +1,63 @@
+package de.usd.cstchef.operations.extractors;
+
+import org.bouncycastle.util.Arrays;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IResponseInfo;
+import de.usd.cstchef.operations.Operation;
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+import de.usd.cstchef.view.ui.VariableTextField;
+
+@OperationInfos(name = "HTTP Cookie", category = OperationCategory.EXTRACTORS, description = "Extracts a cookie from a HTTP request.")
+public class HttpCookieExtractor extends Operation {
+
+ private VariableTextField cookieNameField;
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ byte[] cookieName = cookieNameField.getBytes();
+ if( cookieName.length == 0 )
+ return input;
+
+ byte[] cookieSearch = new byte[cookieName.length + 1];
+ System.arraycopy(cookieName, 0, cookieSearch, 0, cookieName.length);
+ System.arraycopy("=".getBytes(), 0, cookieSearch, cookieName.length, 1);
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+ int length = input.length;
+
+ IResponseInfo resp = helpers.analyzeResponse(input);
+ boolean isRequest = (resp.getStatusCode() == 0);
+
+ String cookieHeader = "\r\nSet-Cookie: ";
+ if(isRequest)
+ cookieHeader = "\r\nCookie: ";
+
+ try {
+
+ int offset = helpers.indexOf(input, cookieHeader.getBytes(), false, 0, length);
+ int line_end = helpers.indexOf(input, "\r\n".getBytes(), false, offset + 2, length);
+ int start = helpers.indexOf(input, cookieSearch, true, offset, line_end);
+ int end = helpers.indexOf(input, ";".getBytes(), true, start, line_end);
+
+ if( end < 0 )
+ end = line_end;
+
+ return Arrays.copyOfRange(input, start + cookieName.length + 1, end);
+
+ } catch( IllegalArgumentException e ) {
+ throw new IllegalArgumentException("Cookie not found.");
+ }
+ }
+
+ @Override
+ public void createUI() {
+ this.cookieNameField = new VariableTextField();
+ this.addUIElement("Name", this.cookieNameField);
+ }
+}
diff --git a/src/de/usd/cstchef/operations/extractors/HttpGetExtractor.java b/src/de/usd/cstchef/operations/extractors/HttpGetExtractor.java
index 07226fe..5a4762b 100644
--- a/src/de/usd/cstchef/operations/extractors/HttpGetExtractor.java
+++ b/src/de/usd/cstchef/operations/extractors/HttpGetExtractor.java
@@ -1,36 +1,43 @@
package de.usd.cstchef.operations.extractors;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
+import java.util.Arrays;
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IParameter;
import de.usd.cstchef.operations.Operation;
-import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.view.ui.VariableTextField;
-@OperationInfos(name = "HTTP GET Parameter", category = OperationCategory.EXTRACTORS, description = "Extracts a GET Parameter of a HTTP request.")
+@OperationInfos(name = "HTTP GET Param", category = OperationCategory.EXTRACTORS, description = "Extracts a GET Parameter of a HTTP request.")
public class HttpGetExtractor extends Operation {
protected VariableTextField parameter;
@Override
protected byte[] perform(byte[] input) throws Exception {
- try {
- // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
- String parameterString = parameter.getText() + '=';
- Reader in = new InputStreamReader(new ByteArrayInputStream(input));
- BufferedReader reader = new BufferedReader(in);
- String requestLine = reader.readLine();
- String[] parts = requestLine.split(" ");
- String params = (parts[1].split("\\?"))[1];
- int start = params.indexOf(parameterString) + parameterString.length();
- int end = (params.indexOf('&', start) > 0) ? params.indexOf('&', start) : params.length();
- return params.substring(start, end).getBytes();
- } catch (Exception e) {
- throw new IllegalArgumentException("Provided input seems not to contain GET parameters.");
- }
+
+ String parameterName = parameter.getText();
+ if( parameterName.equals("") )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+
+ IParameter param = helpers.getRequestParameter(input, parameterName);
+ if( param == null)
+ throw new IllegalArgumentException("Parameter name not found.");
+ if( param.getType() != IParameter.PARAM_URL )
+ throw new IllegalArgumentException("Parameter type is not GET.");
+
+ int start = param.getValueStart();
+ int end = param.getValueEnd();
+
+ byte[] result = Arrays.copyOfRange(input, start, end);
+ return result;
+
}
@Override
@@ -38,5 +45,5 @@ public void createUI() {
this.parameter = new VariableTextField();
this.addUIElement("Parameter", this.parameter);
}
-
+
}
diff --git a/src/de/usd/cstchef/operations/extractors/HttpHeaderExtractor.java b/src/de/usd/cstchef/operations/extractors/HttpHeaderExtractor.java
index ce89488..1783046 100644
--- a/src/de/usd/cstchef/operations/extractors/HttpHeaderExtractor.java
+++ b/src/de/usd/cstchef/operations/extractors/HttpHeaderExtractor.java
@@ -1,51 +1,56 @@
package de.usd.cstchef.operations.extractors;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.util.HashMap;
+import org.bouncycastle.util.Arrays;
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
import de.usd.cstchef.operations.Operation;
-import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.view.ui.VariableTextField;
-@OperationInfos(name = "HTTP header", category = OperationCategory.EXTRACTORS, description = "Extracts a header of a HTTP request.")
+@OperationInfos(name = "HTTP Header", category = OperationCategory.EXTRACTORS, description = "Extracts a header of a HTTP request.")
public class HttpHeaderExtractor extends Operation {
- private VariableTextField headerTxt;
+ private VariableTextField headerNameField;
@Override
protected byte[] perform(byte[] input) throws Exception {
- HashMap headers = new HashMap<>();
-
- try {
-
- Reader in = new InputStreamReader(new ByteArrayInputStream(input));
- BufferedReader reader = new BufferedReader(in);
- reader.readLine(); // skip first line
-
- String header = reader.readLine();
- while (header.length() > 0) {
- String[] values = header.split(":",2);
- headers.put(values[0].trim(), values[1].trim());
- header = reader.readLine();
- }
- String headerKey = headerTxt.getText();
- String headerValue = headers.getOrDefault(headerKey, "");
-
- return headerValue.getBytes();
-
- } catch (Exception e) {
- throw new IllegalArgumentException("Provided input is not a valid http request.");
- }
+
+ byte[] headerName = headerNameField.getBytes();
+ if( headerName.length == 0 )
+ return input;
+
+ byte[] headerSearch = new byte[headerName.length + 4];
+ System.arraycopy("\r\n".getBytes(), 0, headerSearch, 0, 2);
+ System.arraycopy(headerName, 0, headerSearch, 2, headerName.length);
+ System.arraycopy(": ".getBytes(), 0, headerSearch, headerName.length + 2, 2);
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+ int length = input.length;
+
+ int offset = helpers.indexOf(input, headerSearch, true, 0, length);
+
+ if( offset < 0 )
+ throw new IllegalArgumentException("Header not found.");
+
+ int valueStart = helpers.indexOf(input, " ".getBytes(), false, offset, length);
+ if( valueStart < 0 )
+ throw new IllegalArgumentException("Invalid Header format.");
+ int valueEnd = helpers.indexOf(input, "\r\n".getBytes(), false, valueStart, length);
+ if( valueEnd < 0 )
+ throw new IllegalArgumentException("Invalid Header format.");
+
+ byte[] result = Arrays.copyOfRange(input, valueStart + 1, valueEnd);
+ return result;
}
@Override
public void createUI() {
- this.headerTxt = new VariableTextField();
- this.addUIElement("Name", this.headerTxt);
+ this.headerNameField = new VariableTextField();
+ this.addUIElement("Name", this.headerNameField);
}
-
+
}
diff --git a/src/de/usd/cstchef/operations/extractors/HttpJsonExtractor.java b/src/de/usd/cstchef/operations/extractors/HttpJsonExtractor.java
new file mode 100644
index 0000000..ac3acaf
--- /dev/null
+++ b/src/de/usd/cstchef/operations/extractors/HttpJsonExtractor.java
@@ -0,0 +1,48 @@
+package de.usd.cstchef.operations.extractors;
+
+import java.util.Arrays;
+
+import javax.swing.JTextField;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IParameter;
+import de.usd.cstchef.operations.Operation;
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "HTTP JSON", category = OperationCategory.EXTRACTORS, description = "Get a JSON value from HTTP message.")
+public class HttpJsonExtractor extends Operation {
+
+ private JTextField fieldTxt;
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ String keyName = fieldTxt.getText();
+ if( keyName.equals("") )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+
+ IParameter param = helpers.getRequestParameter(input, keyName);
+ if( param == null)
+ throw new IllegalArgumentException("Key not found.");
+ if( param.getType() != IParameter.PARAM_JSON )
+ throw new IllegalArgumentException("Parameter type is not JSON");
+
+ int start = param.getValueStart();
+ int end = param.getValueEnd();
+
+ byte[] result = Arrays.copyOfRange(input, start, end);
+ return result;
+ }
+
+ @Override
+ public void createUI() {
+ this.fieldTxt = new JTextField();
+ this.addUIElement("Field", this.fieldTxt);
+ }
+}
diff --git a/src/de/usd/cstchef/operations/extractors/HttpMethodExtractor.java b/src/de/usd/cstchef/operations/extractors/HttpMethodExtractor.java
index 0af362c..6b21d4b 100644
--- a/src/de/usd/cstchef/operations/extractors/HttpMethodExtractor.java
+++ b/src/de/usd/cstchef/operations/extractors/HttpMethodExtractor.java
@@ -1,13 +1,13 @@
package de.usd.cstchef.operations.extractors;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
+import org.bouncycastle.util.Arrays;
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
import de.usd.cstchef.operations.Operation;
-import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
@OperationInfos(name = "HTTP Method", category = OperationCategory.EXTRACTORS, description = "Extracts the method of a HTTP request.")
public class HttpMethodExtractor extends Operation {
@@ -15,14 +15,17 @@ public class HttpMethodExtractor extends Operation {
@Override
protected byte[] perform(byte[] input) throws Exception {
try {
- // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
- Reader in = new InputStreamReader(new ByteArrayInputStream(input));
- BufferedReader reader = new BufferedReader(in);
- String requestLine = reader.readLine();
- String[] parts = requestLine.split(" ");
- return parts[0].getBytes();
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+ int length = input.length;
+
+ int methodEnd = helpers.indexOf(input, " ".getBytes(), false, 0, length);
+ byte[] result = Arrays.copyOfRange(input, 0, methodEnd);
+
+ return result;
+
} catch (Exception e) {
throw new IllegalArgumentException("Provided input is not a valid http request.");
}
}
-}
\ No newline at end of file
+}
diff --git a/src/de/usd/cstchef/operations/extractors/HttpPostExtractor.java b/src/de/usd/cstchef/operations/extractors/HttpPostExtractor.java
index 3e9ea7b..7a0f171 100644
--- a/src/de/usd/cstchef/operations/extractors/HttpPostExtractor.java
+++ b/src/de/usd/cstchef/operations/extractors/HttpPostExtractor.java
@@ -4,38 +4,44 @@
import burp.BurpUtils;
import burp.IBurpExtenderCallbacks;
-import burp.IRequestInfo;
+import burp.IExtensionHelpers;
+import burp.IParameter;
import de.usd.cstchef.operations.Operation;
-import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
-import de.usd.cstchef.view.ui.VariableTextArea;
+import de.usd.cstchef.operations.OperationCategory;
+import de.usd.cstchef.view.ui.VariableTextField;
-@OperationInfos(name = "HTTP POST Parameter", category = OperationCategory.EXTRACTORS, description = "Extracts a POST parameter of a HTTP request.")
+@OperationInfos(name = "HTTP POST Param", category = OperationCategory.EXTRACTORS, description = "Extracts a POST parameter of a HTTP request.")
public class HttpPostExtractor extends Operation {
- protected VariableTextArea parameter;
+ protected VariableTextField parameter;
@Override
protected byte[] perform(byte[] input) throws Exception {
- try {
- String parameterString = parameter.getText() + "=";
- IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks();
- IRequestInfo requestInfo = cbs.getHelpers().analyzeRequest(input);
- int bodyOffset = requestInfo.getBodyOffset();
-
- String body = new String(Arrays.copyOfRange(input, bodyOffset, input.length));
- int start = body.indexOf(parameterString) + parameterString.length();
- int end = (body.indexOf('&', start) > 0) ? body.indexOf('&', start) : body.length();
- return body.substring(start, end).getBytes();
- } catch (Exception e) {
- throw new IllegalArgumentException("Provided input is not a valid http request.");
- }
+
+ String parameterName = parameter.getText();
+ if( parameterName.equals("") )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+
+ IParameter param = helpers.getRequestParameter(input, parameterName);
+ if( param == null)
+ throw new IllegalArgumentException("Parameter name not found.");
+ if( param.getType() != IParameter.PARAM_BODY )
+ throw new IllegalArgumentException("Parameter type is not POST");
+
+ int start = param.getValueStart();
+ int end = param.getValueEnd();
+
+ byte[] result = Arrays.copyOfRange(input, start, end);
+ return result;
}
@Override
public void createUI() {
- this.parameter = new VariableTextArea();
+ this.parameter = new VariableTextField();
this.addUIElement("Parameter", this.parameter);
}
-
}
diff --git a/src/de/usd/cstchef/operations/extractors/HttpUriExtractor.java b/src/de/usd/cstchef/operations/extractors/HttpUriExtractor.java
index 7cf6881..bea009d 100644
--- a/src/de/usd/cstchef/operations/extractors/HttpUriExtractor.java
+++ b/src/de/usd/cstchef/operations/extractors/HttpUriExtractor.java
@@ -1,28 +1,50 @@
package de.usd.cstchef.operations.extractors;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
+import java.util.Arrays;
+import javax.swing.JCheckBox;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
import de.usd.cstchef.operations.Operation;
-import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
@OperationInfos(name = "HTTP URI", category = OperationCategory.EXTRACTORS, description = "Extracts the URI of a HTTP request.")
public class HttpUriExtractor extends Operation {
+ private JCheckBox checkbox;
+
+ @Override
+ public void createUI() {
+ this.checkbox = new JCheckBox("With parameters");
+ this.checkbox.setSelected(true);
+ this.addUIElement(null, this.checkbox);
+ }
+
@Override
protected byte[] perform(byte[] input) throws Exception {
try {
- // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
- Reader in = new InputStreamReader(new ByteArrayInputStream(input));
- BufferedReader reader = new BufferedReader(in);
- String requestLine = reader.readLine();
- String[] parts = requestLine.split(" ");
- return parts[1].getBytes();
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+ int length = input.length;
+
+ int firstMark = helpers.indexOf(input, " ".getBytes(), false, 0, length);
+ int lineMark = helpers.indexOf(input, " ".getBytes(), false, firstMark + 1, length);
+
+ int secondMark = helpers.indexOf(input, "?".getBytes(), false, firstMark + 1, length);
+
+ if( this.checkbox.isSelected() || secondMark < 0 || secondMark >= lineMark) {
+ secondMark = lineMark;
+ }
+
+ byte[] result = Arrays.copyOfRange(input, firstMark + 1, secondMark);
+ return result;
+
} catch (Exception e) {
throw new IllegalArgumentException("Provided input is not a valid http request.");
}
}
-}
\ No newline at end of file
+}
diff --git a/src/de/usd/cstchef/operations/extractors/HttpXmlExtractor.java b/src/de/usd/cstchef/operations/extractors/HttpXmlExtractor.java
new file mode 100644
index 0000000..f14ef5e
--- /dev/null
+++ b/src/de/usd/cstchef/operations/extractors/HttpXmlExtractor.java
@@ -0,0 +1,48 @@
+package de.usd.cstchef.operations.extractors;
+
+import java.util.Arrays;
+
+import javax.swing.JTextField;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IParameter;
+import de.usd.cstchef.operations.Operation;
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "HTTP XML", category = OperationCategory.EXTRACTORS, description = "Extract XML value from HTTP message.")
+public class HttpXmlExtractor extends Operation {
+
+ private JTextField fieldTxt;
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ String keyName = fieldTxt.getText();
+ if( keyName.equals("") )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+
+ IParameter param = helpers.getRequestParameter(input, keyName);
+ if( param == null)
+ throw new IllegalArgumentException("Key not found.");
+ if( param.getType() != IParameter.PARAM_XML )
+ throw new IllegalArgumentException("Parameter type is not XML");
+
+ int start = param.getValueStart();
+ int end = param.getValueEnd();
+
+ byte[] result = Arrays.copyOfRange(input, start, end);
+ return result;
+ }
+
+ @Override
+ public void createUI() {
+ this.fieldTxt = new JTextField();
+ this.addUIElement("Field", this.fieldTxt);
+ }
+}
diff --git a/src/de/usd/cstchef/operations/extractors/JsonExtractor.java b/src/de/usd/cstchef/operations/extractors/JsonExtractor.java
index 083dfc3..86b6534 100644
--- a/src/de/usd/cstchef/operations/extractors/JsonExtractor.java
+++ b/src/de/usd/cstchef/operations/extractors/JsonExtractor.java
@@ -1,13 +1,14 @@
package de.usd.cstchef.operations.extractors;
import javax.swing.JTextField;
+
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.spi.json.JsonProvider;
import de.usd.cstchef.operations.Operation;
-import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
@OperationInfos(name = "JSON", category = OperationCategory.EXTRACTORS, description = "Extracts values of json objects.")
public class JsonExtractor extends Operation {
@@ -26,10 +27,27 @@ public JsonExtractor() {
@Override
protected byte[] perform(byte[] input) throws Exception {
- Object document = provider.parse(new String(input));
- String result = JsonPath.read(document, fieldTxt.getText());
- return result.getBytes();
+ if( fieldTxt.getText().equals("") )
+ return input;
+
+ Object document = provider.parse(new String(input));
+ Object result = JsonPath.read(document, fieldTxt.getText());
+
+ if( result == null )
+ result = "null";
+
+ Class extends Object> resultClass = result.getClass();
+
+ if( resultClass == String.class ) {
+ return ((String)result).getBytes();
+ } else if( resultClass == Integer.class || resultClass == Float.class || resultClass == Double.class ) {
+ return String.valueOf(result).getBytes();
+ } else if( resultClass == Boolean.class ) {
+ return String.valueOf(result).getBytes();
+ }
+
+ throw new IllegalArgumentException("JSON data of unknown type.");
}
@Override
diff --git a/src/de/usd/cstchef/operations/extractors/RegexExtractor.java b/src/de/usd/cstchef/operations/extractors/RegexExtractor.java
index 176a853..1f16cbd 100644
--- a/src/de/usd/cstchef/operations/extractors/RegexExtractor.java
+++ b/src/de/usd/cstchef/operations/extractors/RegexExtractor.java
@@ -37,6 +37,9 @@ protected byte[] perform(byte[] input) throws Exception {
}
}
+ if( buf.length() > 0 )
+ buf.setLength(buf.length() - 1);
+
return buf.toString().getBytes();
}
@@ -48,5 +51,4 @@ public void createUI() {
this.outputBox = new JComboBox<>(new String[] { LIST_MATCHES, LIST_GROUPS });
this.addUIElement("Output format", this.outputBox);
}
-
-}
\ No newline at end of file
+}
diff --git a/src/de/usd/cstchef/operations/misc/ReadFile.java b/src/de/usd/cstchef/operations/misc/ReadFile.java
index b903d09..3e32828 100644
--- a/src/de/usd/cstchef/operations/misc/ReadFile.java
+++ b/src/de/usd/cstchef/operations/misc/ReadFile.java
@@ -33,7 +33,7 @@ protected byte[] perform(byte[] input) throws Exception {
public void createUI() {
this.fileNameTxt = new VariableTextField();
- this.addUIElement("Variable name", this.fileNameTxt);
+ this.addUIElement("Filename", this.fileNameTxt);
JButton chooseFileButton = new JButton("Select file");
chooseFileButton.addActionListener(this);
diff --git a/src/de/usd/cstchef/operations/misc/WriteFile.java b/src/de/usd/cstchef/operations/misc/WriteFile.java
index ad02708..9f5a550 100644
--- a/src/de/usd/cstchef/operations/misc/WriteFile.java
+++ b/src/de/usd/cstchef/operations/misc/WriteFile.java
@@ -25,7 +25,6 @@ public class WriteFile extends Operation implements ActionListener {
@Override
protected byte[] perform(byte[] input) throws Exception {
String path = fileNameTxt.getText();
- //TODO add ok button? if changed manually, 100 Files are created :D
if (!lastPath.equals(path)) {
if (out != null) {
@@ -48,7 +47,8 @@ protected byte[] perform(byte[] input) throws Exception {
public void createUI() {
this.fileNameTxt = new VariableTextField();
- this.addUIElement("Variable name", this.fileNameTxt);
+ this.fileNameTxt.setEditable(false);
+ this.addUIElement("Filename", this.fileNameTxt);
JButton chooseFileButton = new JButton("Select file");
chooseFileButton.addActionListener(this);
diff --git a/src/de/usd/cstchef/operations/setter/GetSetter.java b/src/de/usd/cstchef/operations/setter/GetSetter.java
deleted file mode 100644
index 73fe30f..0000000
--- a/src/de/usd/cstchef/operations/setter/GetSetter.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package de.usd.cstchef.operations.setter;
-
-import de.usd.cstchef.operations.OperationCategory;
-import de.usd.cstchef.operations.Operation.OperationInfos;
-
-@OperationInfos(name = "HTTP GET Setter", category = OperationCategory.SETTER, description = "Sets the given variable on the given Key.")
-public class GetSetter extends SetterOperation {
-
- @Override
- protected byte[] perform(byte[] input) throws Exception {
- //TODO this has some issues.
-
- String httpRequest = new String(input);
- if (!getWhere().equals("")) {
- return httpRequest.replaceFirst(getWhere() + "=[^(&|\\s)]*", getWhere() + "=" + getWhat()).getBytes();
- }
- else {
- return input;
- }
- }
-}
diff --git a/src/de/usd/cstchef/operations/setter/HttpGetSetter.java b/src/de/usd/cstchef/operations/setter/HttpGetSetter.java
new file mode 100644
index 0000000..a20ebed
--- /dev/null
+++ b/src/de/usd/cstchef/operations/setter/HttpGetSetter.java
@@ -0,0 +1,66 @@
+package de.usd.cstchef.operations.setter;
+
+import javax.swing.JCheckBox;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IParameter;
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "HTTP GET Param", category = OperationCategory.SETTER, description = "Sets a GET parameter to the specified value.")
+public class HttpGetSetter extends SetterOperation {
+
+ private JCheckBox addIfNotPresent;
+ private JCheckBox urlEncode;
+ private JCheckBox urlEncodeAll;
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ String parameterName = getWhere();
+ if( parameterName.equals("") )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+
+ byte[] newValue = getWhatBytes();
+
+ if( urlEncodeAll.isSelected() || urlEncode.isSelected() )
+ newValue = urlEncode(newValue, urlEncodeAll.isSelected(), helpers);
+
+ IParameter param = getParameter(input, parameterName, IParameter.PARAM_URL, helpers);
+
+ if( param == null ) {
+
+ if( !addIfNotPresent.isSelected() )
+ return input;
+
+ param = helpers.buildParameter(parameterName, "dummy", IParameter.PARAM_URL);
+ input = helpers.addParameter(input, param);
+ param = getParameter(input, parameterName, IParameter.PARAM_URL, helpers);
+ }
+
+ byte[] newRequest = replaceParam(input, param, newValue);
+ return newRequest;
+ }
+
+ @Override
+ public void createUI() {
+ super.createUI();
+
+ this.urlEncode = new JCheckBox("URL encode");
+ this.urlEncode.setSelected(false);
+ this.addUIElement(null, this.urlEncode);
+
+ this.urlEncodeAll = new JCheckBox("URL encode all");
+ this.urlEncodeAll.setSelected(false);
+ this.addUIElement(null, this.urlEncodeAll);
+
+ this.addIfNotPresent = new JCheckBox("Add if not present");
+ this.addIfNotPresent.setSelected(true);
+ this.addUIElement(null, this.addIfNotPresent);
+ }
+}
diff --git a/src/de/usd/cstchef/operations/setter/HttpHeaderSetter.java b/src/de/usd/cstchef/operations/setter/HttpHeaderSetter.java
new file mode 100644
index 0000000..f91a080
--- /dev/null
+++ b/src/de/usd/cstchef/operations/setter/HttpHeaderSetter.java
@@ -0,0 +1,78 @@
+package de.usd.cstchef.operations.setter;
+
+import java.util.Arrays;
+
+import javax.swing.JCheckBox;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IRequestInfo;
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "HTTP Header", category = OperationCategory.SETTER, description = "Set a HTTP header to the specified value.")
+public class HttpHeaderSetter extends SetterOperation {
+
+ private JCheckBox addIfNotPresent;
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ byte[] newValue = getWhatBytes();
+ byte[] headerName = getWhereBytes();
+ if( headerName.length == 0 )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+ int length = input.length;
+
+ byte[] headerSearch = new byte[headerName.length + 2];
+ System.arraycopy(headerName, 0, headerSearch, 0, headerName.length);
+ System.arraycopy(": ".getBytes(), 0, headerSearch, headerName.length, 2);
+
+ try {
+
+ int offset = helpers.indexOf(input, headerSearch, false, 0, length);
+ int start = helpers.indexOf(input, ": ".getBytes(), false, offset, length) + 2;
+ int end = helpers.indexOf(input, "\r\n".getBytes(), false, start, length);
+ return insertAtOffset(input, start, end, newValue);
+
+ } catch( IllegalArgumentException e ) {
+
+ if( !addIfNotPresent.isSelected() )
+ return input;
+
+ IRequestInfo info = helpers.analyzeRequest(input);
+ int bodyOffset = info.getBodyOffset() - 2;
+
+ byte[] value = new byte[headerSearch.length + newValue.length + 2];
+ System.arraycopy(headerSearch, 0, value, 0, headerSearch.length);
+ System.arraycopy(newValue, 0, value, headerName.length + 2, newValue.length);
+ System.arraycopy("\r\n".getBytes(), 0, value, headerName.length + 2 + newValue.length, 2);
+ return insertAtOffset(input, bodyOffset, bodyOffset, value);
+
+ }
+ }
+
+ @Override
+ public void createUI() {
+ super.createUI();
+ this.addIfNotPresent = new JCheckBox("Add if not present");
+ this.addIfNotPresent.setSelected(true);
+ this.addUIElement(null, this.addIfNotPresent);
+ }
+
+ private byte[] insertAtOffset(byte[] input, int start, int end, byte[] newValue) {
+ byte[] prefix = Arrays.copyOfRange(input, 0, start);
+ byte[] rest = Arrays.copyOfRange(input, end, input.length);
+
+ byte[] output = new byte[prefix.length + newValue.length + rest.length];
+ System.arraycopy(prefix, 0, output, 0, prefix.length);
+ System.arraycopy(newValue, 0, output, prefix.length, newValue.length);
+ System.arraycopy(rest, 0, output, prefix.length + newValue.length, rest.length);
+
+ return output;
+ }
+}
diff --git a/src/de/usd/cstchef/operations/setter/HttpJsonSetter.java b/src/de/usd/cstchef/operations/setter/HttpJsonSetter.java
new file mode 100644
index 0000000..2ff016b
--- /dev/null
+++ b/src/de/usd/cstchef/operations/setter/HttpJsonSetter.java
@@ -0,0 +1,33 @@
+package de.usd.cstchef.operations.setter;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IParameter;
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "HTTP JSON", category = OperationCategory.SETTER, description = "Set a JSON parameter to the specified value.")
+public class HttpJsonSetter extends SetterOperation {
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ String parameterName = getWhere();
+ if( parameterName.equals("") )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+
+ byte[] newValue = getWhatBytes();
+ IParameter param = getParameter(input, parameterName, IParameter.PARAM_JSON, helpers);
+
+ if( param == null )
+ return input;
+
+ byte[] newRequest = replaceParam(input, param, newValue);
+ return newRequest;
+ }
+
+}
diff --git a/src/de/usd/cstchef/operations/setter/HttpPostSetter.java b/src/de/usd/cstchef/operations/setter/HttpPostSetter.java
new file mode 100644
index 0000000..0a87d74
--- /dev/null
+++ b/src/de/usd/cstchef/operations/setter/HttpPostSetter.java
@@ -0,0 +1,71 @@
+package de.usd.cstchef.operations.setter;
+
+import javax.swing.JCheckBox;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IParameter;
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "HTTP POST Param", category = OperationCategory.SETTER, description = "Set a POST parameter to the specified value.")
+public class HttpPostSetter extends SetterOperation {
+
+ private JCheckBox addIfNotPresent;
+ private JCheckBox urlEncode;
+ private JCheckBox urlEncodeAll;
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ String parameterName = getWhere();
+ if( parameterName.equals("") )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+
+ byte[] newValue = getWhatBytes();
+
+ if( urlEncodeAll.isSelected() || urlEncode.isSelected() )
+ newValue = urlEncode(newValue, urlEncodeAll.isSelected(), helpers);
+
+ IParameter param = getParameter(input, parameterName, IParameter.PARAM_BODY, helpers);
+
+ if( param == null ) {
+
+ if( !addIfNotPresent.isSelected() )
+ return input;
+
+ param = helpers.buildParameter(parameterName, "dummy", IParameter.PARAM_BODY);
+ input = helpers.addParameter(input, param);
+ param = getParameter(input, parameterName, IParameter.PARAM_BODY, helpers);
+ if( param == null )
+ // This case occurs when the HTTP request is a JSON or XML request. Burp does not
+ // support adding parameters to these and therefore the request should stay unmodified.
+ throw new IllegalArgumentException("Failure while adding the parameter. Operation cannot be used on XML or JSON.");
+ }
+
+ byte[] newRequest = replaceParam(input, param, newValue);
+ return newRequest;
+ }
+
+ @Override
+ public void createUI() {
+ super.createUI();
+
+ this.urlEncode = new JCheckBox("URL encode");
+ this.urlEncode.setSelected(false);
+ this.addUIElement(null, this.urlEncode);
+
+ this.urlEncodeAll = new JCheckBox("URL encode all");
+ this.urlEncodeAll.setSelected(false);
+ this.addUIElement(null, this.urlEncodeAll);
+
+ this.addIfNotPresent = new JCheckBox("Add if not present");
+ this.addIfNotPresent.setSelected(true);
+ this.addUIElement(null, this.addIfNotPresent);
+ }
+
+}
diff --git a/src/de/usd/cstchef/operations/string/ReplaceBody.java b/src/de/usd/cstchef/operations/setter/HttpSetBody.java
similarity index 79%
rename from src/de/usd/cstchef/operations/string/ReplaceBody.java
rename to src/de/usd/cstchef/operations/setter/HttpSetBody.java
index 2ee551a..c4ba5ce 100644
--- a/src/de/usd/cstchef/operations/string/ReplaceBody.java
+++ b/src/de/usd/cstchef/operations/setter/HttpSetBody.java
@@ -1,4 +1,4 @@
-package de.usd.cstchef.operations.string;
+package de.usd.cstchef.operations.setter;
import java.util.Arrays;
@@ -10,8 +10,8 @@
import de.usd.cstchef.operations.Operation.OperationInfos;
import de.usd.cstchef.view.ui.FormatTextField;
-@OperationInfos(name = "ReplaceBody", category = OperationCategory.STRING, description = "Replaces HTTP body")
-public class ReplaceBody extends Operation {
+@OperationInfos(name = "HTTP Body", category = OperationCategory.SETTER, description = "Set the HTTP body to the specified value.")
+public class HttpSetBody extends Operation {
private FormatTextField replacementTxt;
@@ -33,7 +33,7 @@ protected byte[] perform(byte[] input) throws Exception {
@Override
public void createUI() {
this.replacementTxt = new FormatTextField();
- this.addUIElement("Replacement", this.replacementTxt);
+ this.addUIElement("Body", this.replacementTxt);
}
}
diff --git a/src/de/usd/cstchef/operations/setter/HttpSetCookie.java b/src/de/usd/cstchef/operations/setter/HttpSetCookie.java
new file mode 100644
index 0000000..23641ae
--- /dev/null
+++ b/src/de/usd/cstchef/operations/setter/HttpSetCookie.java
@@ -0,0 +1,104 @@
+package de.usd.cstchef.operations.setter;
+
+import java.util.Arrays;
+
+import javax.swing.JCheckBox;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IResponseInfo;
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "HTTP Cookie", category = OperationCategory.SETTER, description = "Set a HTTP cookie to the specified value.")
+public class HttpSetCookie extends SetterOperation {
+
+ private JCheckBox addIfNotPresent;
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ byte[] newValue = getWhatBytes();
+ byte[] cookieName = getWhereBytes();
+ if( cookieName.length == 0 )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+ int length = input.length;
+
+ byte[] cookieSearch = new byte[cookieName.length + 1];
+ System.arraycopy(cookieName, 0, cookieSearch, 0, cookieName.length);
+ System.arraycopy("=".getBytes(), 0, cookieSearch, cookieName.length, 1);
+
+ IResponseInfo resp = helpers.analyzeResponse(input);
+ boolean isRequest = (resp.getStatusCode() == 0);
+
+ String cookieHeader = "\r\nSet-Cookie: ";
+ if(isRequest)
+ cookieHeader = "\r\nCookie: ";
+
+ int offset = -1;
+ int cookieHeaderLength = cookieHeader.length();
+
+ try {
+
+ offset = helpers.indexOf(input, cookieHeader.getBytes(), false, 0, length);
+ int line_end = helpers.indexOf(input, "\r\n".getBytes(), false, offset + 2, length);
+ int start = helpers.indexOf(input, cookieSearch, true, offset, line_end);
+ int end = helpers.indexOf(input, ";".getBytes(), true, start, line_end);
+
+ if( end < 0 )
+ end = line_end;
+
+ return insertAtOffset(input, start + cookieSearch.length, end, newValue);
+
+ } catch( IllegalArgumentException e ) {
+
+ if( !addIfNotPresent.isSelected() )
+ return input;
+
+ if( (offset > 0) && isRequest ) {
+
+ byte[] value = new byte[cookieName.length + newValue.length + 3];
+ System.arraycopy(cookieName, 0, value, 0, cookieName.length);
+ System.arraycopy("=".getBytes(), 0, value, cookieName.length, 1);
+ System.arraycopy(newValue, 0, value, cookieName.length + 1, newValue.length);
+ System.arraycopy("; ".getBytes(), 0, value, cookieName.length + 1 + newValue.length, 2);
+ return insertAtOffset(input, offset + cookieHeaderLength, offset + cookieHeaderLength, value);
+
+ } else {
+
+ int bodyOffset = resp.getBodyOffset() - 4;
+ byte[] value = new byte[cookieName.length + newValue.length + cookieHeaderLength + 2];
+ System.arraycopy(cookieHeader.getBytes(), 0, value, 0, cookieHeaderLength);
+ System.arraycopy(cookieName, 0, value, cookieHeaderLength, cookieName.length);
+ System.arraycopy("=".getBytes(), 0, value, cookieHeaderLength + cookieName.length, 1);
+ System.arraycopy(newValue, 0, value, cookieHeaderLength + cookieName.length + 1, newValue.length);
+ System.arraycopy(";".getBytes(), 0, value, cookieHeaderLength + cookieName.length + 1 + newValue.length, 1);
+ return insertAtOffset(input, bodyOffset, bodyOffset, value);
+ }
+ }
+ }
+
+ @Override
+ public void createUI() {
+ super.createUI();
+ this.addIfNotPresent = new JCheckBox("Add if not present");
+ this.addIfNotPresent.setSelected(true);
+ this.addUIElement(null, this.addIfNotPresent);
+ }
+
+ private byte[] insertAtOffset(byte[] input, int start, int end, byte[] newValue) {
+ byte[] prefix = Arrays.copyOfRange(input, 0, start);
+ byte[] rest = Arrays.copyOfRange(input, end, input.length);
+
+ byte[] output = new byte[prefix.length + newValue.length + rest.length];
+ System.arraycopy(prefix, 0, output, 0, prefix.length);
+ System.arraycopy(newValue, 0, output, prefix.length, newValue.length);
+ System.arraycopy(rest, 0, output, prefix.length + newValue.length, rest.length);
+
+ return output;
+ }
+}
diff --git a/src/de/usd/cstchef/operations/setter/HttpSetUri.java b/src/de/usd/cstchef/operations/setter/HttpSetUri.java
index 5950dc2..718958a 100644
--- a/src/de/usd/cstchef/operations/setter/HttpSetUri.java
+++ b/src/de/usd/cstchef/operations/setter/HttpSetUri.java
@@ -1,45 +1,61 @@
package de.usd.cstchef.operations.setter;
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
+import java.util.Arrays;
+import javax.swing.JCheckBox;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
import de.usd.cstchef.operations.Operation;
-import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.view.ui.VariableTextField;
-@OperationInfos(name = "HTTP SET Uri", category = OperationCategory.SETTER, description = "Sets the given variable as the uri.")
+@OperationInfos(name = "HTTP URI", category = OperationCategory.SETTER, description = "Sets the specified variable as the uri.")
public class HttpSetUri extends Operation {
private VariableTextField uriTxt;
+ private JCheckBox checkbox;
@Override
public void createUI() {
this.uriTxt = new VariableTextField();
this.addUIElement("Uri", this.uriTxt);
+
+ this.checkbox = new JCheckBox("Keep parameters");
+ this.checkbox.setSelected(false);
+ this.addUIElement(null, this.checkbox);
}
+
@Override
protected byte[] perform(byte[] input) throws Exception {
try {
- // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
- String newUri = this.uriTxt.getText();
- Reader in = new InputStreamReader(new ByteArrayInputStream(input));
- BufferedReader reader = new BufferedReader(in);
- String requestLine = reader.readLine();
- String[] parts = requestLine.split(" ", 3);
- StringBuffer buf = new StringBuffer();
- String newRequestLine = String.format("%s %s %s", parts[0], newUri, parts[2]);
- // TODO is there a better way to do this?
- buf.append(newRequestLine).append("\n");
- String line;
- while ((line = reader.readLine()) != null) {
- buf.append(line).append("\n");
- }
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+ int length = input.length;
+
+ int firstMark = helpers.indexOf(input, " ".getBytes(), false, 0, length);
+ int lineMark = helpers.indexOf(input, " ".getBytes(), false, firstMark + 1, length);
+
+ int secondMark = helpers.indexOf(input, "?".getBytes(), false, firstMark + 1, length);
+
+ if( !this.checkbox.isSelected() || secondMark < 0 || secondMark >= lineMark ) {
+ secondMark = lineMark;
+ }
+
+ byte[] method = Arrays.copyOfRange(input, 0, firstMark + 1);
+ byte[] newUri = this.uriTxt.getBytes();
+ byte[] rest = Arrays.copyOfRange(input, secondMark, length);
+
+ byte[] newRequest = new byte[method.length + newUri.length + rest.length];
+ System.arraycopy(method, 0, newRequest, 0, method.length);
+ System.arraycopy(newUri, 0, newRequest, method.length, newUri.length);
+ System.arraycopy(rest, 0, newRequest, method.length + newUri.length, rest.length);
+
+ return newRequest;
- return buf.toString().getBytes();
} catch (Exception e) {
throw new IllegalArgumentException("Provided input is not a valid http request.");
}
diff --git a/src/de/usd/cstchef/operations/setter/HttpXmlSetter.java b/src/de/usd/cstchef/operations/setter/HttpXmlSetter.java
new file mode 100644
index 0000000..b783db9
--- /dev/null
+++ b/src/de/usd/cstchef/operations/setter/HttpXmlSetter.java
@@ -0,0 +1,33 @@
+package de.usd.cstchef.operations.setter;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IParameter;
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+
+@OperationInfos(name = "HTTP XML", category = OperationCategory.SETTER, description = "Set a XML parameter to the specified value.")
+public class HttpXmlSetter extends SetterOperation {
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ String parameterName = getWhere();
+ if( parameterName.equals("") )
+ return input;
+
+ IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = callbacks.getHelpers();
+
+ byte[] newValue = getWhatBytes();
+ IParameter param = getParameter(input, parameterName, IParameter.PARAM_XML, helpers);
+
+ if( param == null )
+ return input;
+
+ byte[] newRequest = replaceParam(input, param, newValue);
+ return newRequest;
+ }
+
+}
diff --git a/src/de/usd/cstchef/operations/setter/JsonSetter.java b/src/de/usd/cstchef/operations/setter/JsonSetter.java
new file mode 100644
index 0000000..4afe94f
--- /dev/null
+++ b/src/de/usd/cstchef/operations/setter/JsonSetter.java
@@ -0,0 +1,91 @@
+package de.usd.cstchef.operations.setter;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+
+import javax.swing.JCheckBox;
+
+import com.jayway.jsonpath.DocumentContext;
+import com.jayway.jsonpath.JsonPath;
+
+import de.usd.cstchef.operations.Operation.OperationInfos;
+import de.usd.cstchef.operations.OperationCategory;
+import de.usd.cstchef.view.ui.VariableTextField;
+
+@OperationInfos(name = "JSON", category = OperationCategory.SETTER, description = "Set value of json object.")
+public class JsonSetter extends SetterOperation implements ActionListener {
+
+ private JCheckBox addIfNotPresent;
+ private VariableTextField path;
+
+ @Override
+ protected byte[] perform(byte[] input) throws Exception {
+
+ if( getWhere().equals("") )
+ return input;
+
+ DocumentContext document = JsonPath.parse(new String(input));
+
+ try {
+ document.read(getWhere());
+ } catch( Exception e ) {
+
+ if( !addIfNotPresent.isSelected() )
+ throw new IllegalArgumentException("Key not found.");
+
+ String insertPath = this.path.getText();
+ if( insertPath.equals("Insert-Path") || insertPath.equals("") )
+ insertPath = "$";
+
+ document = document.put(insertPath, getWhere(), getWhat());
+ return document.jsonString().getBytes();
+ }
+
+ document.set(getWhere(), getWhat());
+ return document.jsonString().getBytes();
+ }
+
+ @Override
+ public void createUI() {
+ super.createUI();
+ this.addIfNotPresent = new JCheckBox("Add if not present");
+ this.addIfNotPresent.setSelected(true);
+ this.addIfNotPresent.addActionListener(this);
+ this.addUIElement(null, this.addIfNotPresent);
+
+ this.path = new VariableTextField();
+ this.path.setText("Insert-Path");
+ this.path.setForeground(Color.GRAY);
+ this.path.addFocusListener(new FocusListener() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ if (path.getText().equals("Insert-Path")) {
+ path.setText("");
+ path.setForeground(null);
+ }
+ }
+ @Override
+ public void focusLost(FocusEvent e) {
+ if (path.getText().isEmpty()) {
+ path.setForeground(Color.GRAY);
+ path.setText("Insert-Path");
+ }
+ }
+ });
+ this.addUIElement(null, this.path);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent arg0) {
+ if( arg0.getSource() == this.addIfNotPresent ) {
+ if( this.addIfNotPresent.isSelected() ) {
+ this.path.setEditable(true);
+ } else {
+ this.path.setEditable(false);
+ }
+ }
+ }
+}
diff --git a/src/de/usd/cstchef/operations/setter/SetterOperation.java b/src/de/usd/cstchef/operations/setter/SetterOperation.java
index ff47814..b3be909 100644
--- a/src/de/usd/cstchef/operations/setter/SetterOperation.java
+++ b/src/de/usd/cstchef/operations/setter/SetterOperation.java
@@ -1,5 +1,15 @@
package de.usd.cstchef.operations.setter;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.bouncycastle.util.encoders.Hex;
+
+import burp.IExtensionHelpers;
+import burp.IParameter;
+import burp.IRequestInfo;
import de.usd.cstchef.operations.Operation;
import de.usd.cstchef.view.ui.VariableTextField;
@@ -12,16 +22,83 @@ public abstract class SetterOperation extends Operation {
public void createUI() {
this.whereToSet = new VariableTextField();
this.whatToSet = new VariableTextField();
- this.addUIElement("Where to Set", this.whereToSet);
- this.addUIElement("What to Set", this.whatToSet);
+ this.addUIElement("Key", this.whereToSet);
+ this.addUIElement("Value", this.whatToSet);
}
protected String getWhere() {
return whereToSet.getText();
}
+ protected byte[] getWhereBytes() {
+ return whereToSet.getBytes();
+ }
+
protected String getWhat() {
return whatToSet.getText();
}
+ protected byte[] getWhatBytes() {
+ return whatToSet.getBytes();
+ }
+
+ // This is required because Burps getRequestParameter returns always the first occurrence of the parameter name.
+ // If you have e.g. a cookie with the same name as the POST parameter, you have no chance of getting the POST
+ // parameter using getRequestParameter (at least I do not know how).
+ protected IParameter getParameter(byte[] request, String paramName, byte type, IExtensionHelpers helpers) {
+
+ IRequestInfo info = helpers.analyzeRequest(request);
+ List parameters = info.getParameters();
+ IParameter param = null;
+
+ for(IParameter p:parameters) {
+ if( p.getName().equals(paramName) )
+ if( p.getType() == type ) {
+ param = p;
+ break;
+ }
+ }
+ return param;
+ }
+
+ protected byte[] urlEncode(byte[] input, boolean all, IExtensionHelpers helpers) throws IOException {
+
+ byte[] newValue = input;
+
+ if( all ) {
+ byte[] delimiter = "%".getBytes();
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ out.write(delimiter);
+
+ for (int i = 0; i < newValue.length - 1; i++) {
+ out.write(Hex.encode(new byte[] { newValue[i] }));
+ out.write(delimiter);
+ }
+
+ out.write(Hex.encode(new byte[] { newValue[newValue.length - 1] }));
+ newValue = out.toByteArray();
+
+ } else {
+ newValue = helpers.urlEncode(input);
+ }
+
+ return newValue;
+ }
+
+ protected byte[] replaceParam(byte[] request, IParameter param, byte[] newValue) {
+
+ int length = request.length;
+ int start = param.getValueStart();
+ int end = param.getValueEnd();
+
+ byte[] prefix = Arrays.copyOfRange(request, 0, start);
+ byte[] rest = Arrays.copyOfRange(request, end, length);
+
+ byte[] newRequest = new byte[prefix.length + newValue.length + rest.length];
+ System.arraycopy(prefix, 0, newRequest, 0, prefix.length);
+ System.arraycopy(newValue, 0, newRequest, prefix.length, newValue.length);
+ System.arraycopy(rest, 0, newRequest, prefix.length + newValue.length, rest.length);
+
+ return newRequest;
+ }
}
diff --git a/src/de/usd/cstchef/operations/string/Replace.java b/src/de/usd/cstchef/operations/string/Replace.java
index 0e6ad02..c891707 100644
--- a/src/de/usd/cstchef/operations/string/Replace.java
+++ b/src/de/usd/cstchef/operations/string/Replace.java
@@ -1,5 +1,10 @@
package de.usd.cstchef.operations.string;
+import javax.swing.JCheckBox;
+
+import burp.BurpUtils;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
import de.usd.cstchef.operations.Operation;
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
@@ -9,24 +14,51 @@
@OperationInfos(name = "Replace", category = OperationCategory.STRING, description = "Uses a regular expression to replace all occurences. Has side effect on binary content due to String Encoding.")
public class Replace extends Operation {
- private VariableTextField regexTxt;
+ private JCheckBox checkbox;
+ private VariableTextField exptTxt;
private VariableTextArea replacementTxt;
@Override
protected byte[] perform(byte[] input) throws Exception {
- String inputStr = new String(input);
- String result = inputStr.replaceAll(regexTxt.getText(), replacementTxt.getText());
- return result.getBytes();
+ byte[] result = null;
+ if( checkbox.isSelected() ) {
+ String inputStr = new String(input);
+ result = inputStr.replaceAll(exptTxt.getText(), replacementTxt.getText()).getBytes();
+ } else {
+ IBurpExtenderCallbacks cbs = BurpUtils.getInstance().getCallbacks();
+ IExtensionHelpers helpers = cbs.getHelpers();
+
+ int start = helpers.indexOf(input, exptTxt.getBytes(), true, 0, input.length);
+
+ if(start < 0)
+ return input;
+
+ byte[] replaced = exptTxt.getBytes();
+ byte[] replacement = replacementTxt.getBytes();
+
+ byte[] newRequest = new byte[input.length + replacement.length - replaced.length];
+ System.arraycopy(input, 0, newRequest, 0, start);
+ System.arraycopy(replacement, 0, newRequest, start, replacement.length);
+ System.arraycopy(input, start + replaced.length, newRequest, start + replacement.length, input.length - replaced.length - start);
+
+ result = newRequest;
+ }
+
+ return result;
}
@Override
public void createUI() {
- this.regexTxt = new VariableTextField();
- this.addUIElement("Regex", this.regexTxt);
+ this.exptTxt = new VariableTextField();
+ this.addUIElement("Expr", this.exptTxt);
+ this.checkbox = new JCheckBox("Regex");
+ this.checkbox.setSelected(false);
+ this.addUIElement(null, this.checkbox);
+
this.replacementTxt = new VariableTextArea();
this.addUIElement("Value", this.replacementTxt);
}
-}
\ No newline at end of file
+}
diff --git a/src/de/usd/cstchef/operations/string/StaticString.java b/src/de/usd/cstchef/operations/string/StaticString.java
index 6dafc51..a958894 100644
--- a/src/de/usd/cstchef/operations/string/StaticString.java
+++ b/src/de/usd/cstchef/operations/string/StaticString.java
@@ -12,7 +12,7 @@ public class StaticString extends Operation {
@Override
protected byte[] perform(byte[] input) throws Exception {
- return this.stringTxt.getText().trim().getBytes();
+ return this.stringTxt.getBytes();
}
@Override
@@ -21,4 +21,4 @@ public void createUI() {
this.addUIElement("Value", this.stringTxt);
}
-}
\ No newline at end of file
+}
diff --git a/src/de/usd/cstchef/operations/string/Substring.java b/src/de/usd/cstchef/operations/string/Substring.java
index 34fc029..3a984d0 100644
--- a/src/de/usd/cstchef/operations/string/Substring.java
+++ b/src/de/usd/cstchef/operations/string/Substring.java
@@ -2,6 +2,8 @@
import javax.swing.JSpinner;
+import org.bouncycastle.util.Arrays;
+
import de.usd.cstchef.operations.Operation;
import de.usd.cstchef.operations.OperationCategory;
import de.usd.cstchef.operations.Operation.OperationInfos;
@@ -14,11 +16,19 @@ public class Substring extends Operation {
@Override
protected byte[] perform(byte[] input) throws Exception {
- String inputStr = new String(input);
+
int start = (int) startSpinner.getValue();
int end = (int) endSpinner.getValue();
-
- return inputStr.substring(start, end).getBytes();
+
+ if( start < 0 )
+ start = input.length + start;
+ if( end < 0 )
+ end = input.length + end;
+ if( end > input.length )
+ end = input.length + 1;
+
+ byte[] slice = Arrays.copyOfRange(input, start, end);
+ return slice;
}
@Override
@@ -30,4 +40,4 @@ public void createUI() {
this.addUIElement("End", this.endSpinner);
}
-}
\ No newline at end of file
+}
diff --git a/src/de/usd/cstchef/view/BurpEditorWrapper.java b/src/de/usd/cstchef/view/BurpEditorWrapper.java
index 66ea78c..4d6af6c 100644
--- a/src/de/usd/cstchef/view/BurpEditorWrapper.java
+++ b/src/de/usd/cstchef/view/BurpEditorWrapper.java
@@ -10,6 +10,7 @@
import burp.BurpUtils;
import burp.IMessageEditor;
+import burp.IMessageEditorController;
public class BurpEditorWrapper implements IMessageEditor, DocumentListener {
@@ -19,9 +20,9 @@ public class BurpEditorWrapper implements IMessageEditor, DocumentListener {
boolean isModified;
byte[] lastContent;
- public BurpEditorWrapper(boolean editable) {
+ public BurpEditorWrapper(IMessageEditorController controller, boolean editable) {
if (BurpUtils.inBurp()) {
- this.burpEditor = BurpUtils.getInstance().getCallbacks().createMessageEditor(null, editable);
+ this.burpEditor = BurpUtils.getInstance().getCallbacks().createMessageEditor(controller, editable);
fallbackMode = false;
} else {
this.fallbackArea = new JTextArea();
diff --git a/src/de/usd/cstchef/view/OperationsTree.java b/src/de/usd/cstchef/view/OperationsTree.java
index 555b902..2bc26fc 100644
--- a/src/de/usd/cstchef/view/OperationsTree.java
+++ b/src/de/usd/cstchef/view/OperationsTree.java
@@ -6,8 +6,10 @@
import java.util.Enumeration;
import java.util.HashMap;
+import javax.swing.ImageIcon;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
@@ -22,13 +24,20 @@
public class OperationsTree extends JTree {
private DefaultTreeModel model;
-
+ private static ImageIcon nodeIcon = new ImageIcon(Operation.class.getResource("/operation.png"));
+ private static ImageIcon openIcon = new ImageIcon(Operation.class.getResource("/folder_open.png"));
+ private static ImageIcon closedIcon = new ImageIcon(Operation.class.getResource("/folder_closed.png"));
+
public OperationsTree() {
super();
this.model = (DefaultTreeModel) this.getModel();
this.model.setRoot(this.createTree());
this.setToolTipText("");
+ DefaultTreeCellRenderer renderer = (DefaultTreeCellRenderer) this.getCellRenderer();
+ renderer.setLeafIcon(nodeIcon);
+ renderer.setClosedIcon(closedIcon);
+ renderer.setOpenIcon(openIcon);
}
@Override
@@ -144,5 +153,4 @@ private void expandAll(TreePath path) {
}
this.expandPath(path);
}
-
}
diff --git a/src/de/usd/cstchef/view/RecipePanel.java b/src/de/usd/cstchef/view/RecipePanel.java
index 0d729ce..58886a7 100644
--- a/src/de/usd/cstchef/view/RecipePanel.java
+++ b/src/de/usd/cstchef/view/RecipePanel.java
@@ -18,8 +18,6 @@
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import javax.swing.BorderFactory;
import javax.swing.JButton;
@@ -43,10 +41,13 @@
import com.fasterxml.jackson.databind.node.ObjectNode;
import burp.BurpUtils;
+import burp.CstcMessageEditorController;
import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.IHttpRequestResponse;
+import burp.IParameter;
import burp.IRequestInfo;
import burp.Logger;
-import de.usd.cstchef.Utils;
import de.usd.cstchef.VariableStore;
import de.usd.cstchef.operations.Operation;
@@ -56,6 +57,7 @@ public class RecipePanel extends JPanel implements ChangeListener {
private int operationSteps = 10;
private boolean autoBake = true;
+ private boolean isRequest = true;
private int bakeThreshold = 400;
private String recipeName;
private int filterMask;
@@ -66,11 +68,15 @@ public class RecipePanel extends JPanel implements ChangeListener {
private JPanel operationLines;
private RequestFilterDialog requestFilterDialog;
+ private CstcMessageEditorController controllerOrig;
+ private CstcMessageEditorController controllerMod;
+
private Timer bakeTimer;
- public RecipePanel(String recipeName) {
+ public RecipePanel(String recipeName, boolean isRequest) {
this.recipeName = recipeName;
+ this.isRequest = isRequest;
ToolTipManager tooltipManager = ToolTipManager.sharedInstance();
tooltipManager.setInitialDelay(0);
@@ -78,14 +84,17 @@ public RecipePanel(String recipeName) {
JSplitPane inOut = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
+ controllerOrig = new CstcMessageEditorController();
+ controllerMod = new CstcMessageEditorController();
+
// create input panel
JPanel inputPanel = new LayoutPanel("Input");
- inputText = new BurpEditorWrapper(true);
+ inputText = new BurpEditorWrapper(controllerOrig, true);
inputPanel.add(inputText.getComponent());
// create output panel
JPanel outputPanel = new LayoutPanel("Output");
- outputText = new BurpEditorWrapper(false);
+ outputText = new BurpEditorWrapper(controllerMod, false);
outputPanel.add(outputText.getComponent());
JPanel searchTreePanel = new JPanel();
@@ -297,8 +306,19 @@ private void autoSaveToBurp() {
}
}
- public void setInput(String input) {
- this.inputText.setMessage(input.getBytes(), false);
+ public void setInput(IHttpRequestResponse requestResponse) {
+ if( isRequest )
+ this.inputText.setMessage(requestResponse.getRequest(), true);
+ else {
+ byte[] responseBytes = requestResponse.getResponse();
+ if( responseBytes == null )
+ responseBytes = "Your request has no server response yet :(".getBytes();
+ this.inputText.setMessage(responseBytes, false);
+ }
+
+ this.controllerOrig.setHttpRequestResponse(requestResponse);
+ this.controllerMod.setHttpRequestResponse(requestResponse);
+
this.bake(false);
}
@@ -397,18 +417,36 @@ private byte[] doBake(byte[] input) {
if (BurpUtils.inBurp()) {
IBurpExtenderCallbacks callbacks = BurpUtils.getInstance().getCallbacks();
- IRequestInfo info = callbacks.getHelpers().analyzeRequest(result);
- int contentLen = result.length - info.getBodyOffset();
- String headers = new String(result, 0, info.getBodyOffset());
- Pattern p = Pattern.compile("Content-Length:\\s*(\\d*)");
- Matcher m = p.matcher(headers);
- if (m.find()) {
- headers = m.replaceFirst("Content-Length: " + String.valueOf(contentLen));
+ IExtensionHelpers helpers = callbacks.getHelpers();
+
+ IRequestInfo info;
+ try {
+ info = helpers.analyzeRequest(result);
+ } catch( IllegalArgumentException e ) {
+ // In this case there is no valid HTTP request and no Content-Length update is requried.
+ return result;
+ }
+
+ List headers = info.getHeaders();
+ int offset = info.getBodyOffset();
+
+ if( result.length == offset ) {
+ // In this case there is no body and we do not need to update the content length header.
+ return result;
+ }
+
+ for(String header : headers) {
+ if(header.startsWith("Content-Length:")) {
+ // To update the content-length header, we just add a dummy parameter and remove it right away.
+ // Burps extension helpers will care about updating the length without any string transformations.
+ IParameter dummy = helpers.buildParameter("dummy", "dummy", IParameter.PARAM_BODY);
+ result = helpers.addParameter(result, dummy);
+ result = helpers.removeParameter(result, dummy);
+ break;
+ }
}
- byte[] request = new byte[headers.length() + contentLen];
- System.arraycopy(headers.getBytes(), 0, request, 0, headers.length());
- System.arraycopy(result, info.getBodyOffset(), request, headers.length(), contentLen);
- return request;
+ return result;
+
} else {
return result;
}
@@ -427,7 +465,13 @@ public void run() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
- outputText.setMessage(result, true);
+ if( isRequest) {
+ outputText.setMessage(result, true);
+ controllerMod.setRequest(result);
+ } else {
+ outputText.setMessage(result, false);
+ controllerMod.setResponse(result);
+ }
VariablesWindow vw = VariablesWindow.getInstance();
if (vw.isVisible()) {
vw.refresh(variables);
diff --git a/src/de/usd/cstchef/view/View.java b/src/de/usd/cstchef/view/View.java
index e65644c..22faa09 100644
--- a/src/de/usd/cstchef/view/View.java
+++ b/src/de/usd/cstchef/view/View.java
@@ -22,9 +22,9 @@ public View() {
this.setLayout(new BorderLayout());
JTabbedPane tabbedPane = new JTabbedPane();
- incomingRecipePanel = new RecipePanel("Incomming");
- outgoingRecipePanel = new RecipePanel("Outgoing");
- formatRecipePanel = new RecipePanel("Formatting");
+ incomingRecipePanel = new RecipePanel("Incomming", false);
+ outgoingRecipePanel = new RecipePanel("Outgoing", true);
+ formatRecipePanel = new RecipePanel("Formatting", true);
tabbedPane.addTab("Outgoing Requests", null, outgoingRecipePanel, "Outgoing requests from the browser, the repeater or another tool.");
tabbedPane.addTab("Incoming Responses", null, incomingRecipePanel, "Responses from the server.");
diff --git a/src/de/usd/cstchef/view/ui/FormatTextField.java b/src/de/usd/cstchef/view/ui/FormatTextField.java
index f1dc234..53e0b39 100644
--- a/src/de/usd/cstchef/view/ui/FormatTextField.java
+++ b/src/de/usd/cstchef/view/ui/FormatTextField.java
@@ -25,7 +25,7 @@ public FormatTextField() {
this.setLayout(new BorderLayout());
this.setBackground(new Color(0, 0, 0, 0));
this.txtField = new VariableTextField();
- this.formatBox = new JComboBox<>(new String[] {"UTF-8", "Hex", "Latin1", "Base64"});
+ this.formatBox = new JComboBox<>(new String[] {"Raw", "UTF-8", "Hex", "Latin1", "Base64"});
this.formatBox.addActionListener(this);
Box box = Box.createHorizontalBox();
@@ -51,21 +51,25 @@ public void setValues(Map values) {
}
public byte[] getText() throws UnsupportedEncodingException {
- String text = this.txtField.getText();
+
+ byte[] raw = this.txtField.getBytes();
byte[] result = null;
switch ((String) this.formatBox.getSelectedItem()) {
+ case "Raw":
+ result = raw;
+ break;
case "Hex":
- result = Hex.decode(text);
+ result = Hex.decode(raw);
break;
case "Base64":
- result = Base64.getDecoder().decode(text);
+ result = Base64.getDecoder().decode(raw);
break;
case "Latin1":
- result = text.getBytes("ISO-8859-1");
+ result = this.txtField.getText().getBytes("ISO-8859-1");
break;
case "UTF-8":
- result = text.getBytes("UTF-8");
+ result = this.txtField.getText().getBytes("UTF-8");
break;
}
return result;
diff --git a/src/de/usd/cstchef/view/ui/VariableTextArea.java b/src/de/usd/cstchef/view/ui/VariableTextArea.java
index f7334e3..0b8b1c3 100644
--- a/src/de/usd/cstchef/view/ui/VariableTextArea.java
+++ b/src/de/usd/cstchef/view/ui/VariableTextArea.java
@@ -23,6 +23,11 @@ public String getText() {
String text = this.txtArea.getText();
return Utils.replaceVariables(text);
}
+
+ public byte[] getBytes() {
+ byte[] bytes = this.txtArea.getText().getBytes();
+ return Utils.replaceVariablesByte(bytes);
+ }
public void setText(String text) {
this.txtArea.setText(text);
diff --git a/src/de/usd/cstchef/view/ui/VariableTextField.java b/src/de/usd/cstchef/view/ui/VariableTextField.java
index 15f1700..c9f2caf 100644
--- a/src/de/usd/cstchef/view/ui/VariableTextField.java
+++ b/src/de/usd/cstchef/view/ui/VariableTextField.java
@@ -17,6 +17,11 @@ public String getText() {
String text = super.getText();
return Utils.replaceVariables(text);
}
+
+ public byte[] getBytes() {
+ byte[] bytes = super.getText().getBytes();
+ return Utils.replaceVariablesByte(bytes);
+ }
public String getRawText() {
return super.getText();