ZAP missing payload mode pitchfork - zap

I started using ZAP and I really like it so far but I miss an option or maybe I don't find it. Burp has an payload mode called "pitchfork" where you can increment two payloads at a time. Got ZAP anything like this?
Thanks

I just realized that what I'd given you was actually Battering ram not Pitchfork.
https://portswigger.net/burp/documentation/desktop/tools/intruder/attack-types
For Pitchfork you'd simply define two fuzz locations and specify two different lists. Easy peasy.
Here's how you'd accomplish what you need.
Assume the following request for my answer/example:
GET http://localhost:8090/bodgeit/product.jsp?typeid=3&foo=3 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Host: localhost:8090
Let's say that typeid and foo are the param values that you want to pitchfork. Your going to create a Payload Generator script in ZAP, such as the following (this is a simple minor tweak to the default template, the important differences are outlined below after the code sample):
// Auxiliary variables/constants for payload generation.
var NUMBER_OF_PAYLOADS = 10;
var INITIAL_VALUE = 1;
var count = INITIAL_VALUE;
var MID= '&foo='
/**
* Returns the number of generated payloads, zero to indicate unknown number.
* The number is used as a hint for progress calculations.
*
* #return {number} The number of generated payloads.
*/
function getNumberOfPayloads() {
return NUMBER_OF_PAYLOADS;
}
/**
* Returns true if there are still payloads to generate, false otherwise.
*
* Called before each call to next().
*
* #return {boolean} If there are still payloads to generate.
*/
function hasNext() {
return (count <= NUMBER_OF_PAYLOADS);
}
/**
* Returns the next generated payload.
*
* This method is called while hasNext() returns true.
*
* #return {string} The next generated payload.
*/
function next() {
payload = count;
count++;
return payload+MID+payload;
}
/**
* Resets the internal state of the payload generator, as if no calls to
* hasNext() or next() have been previously made.
*
* Normally called once the method hasNext() returns false and while payloads
* are still needed.
*/
function reset() {
count = INITIAL_VALUE;
}
/**
* Releases any resources used for generation of payloads (for example, a file).
*
* Called once the payload generator is no longer needed.
*/
function close() {
}
Note: Declaration of the MID constant, which is the middle part of the string between the two param values. Modification of the next() method which returns the same value for both param values with the "MID" string inserted between.
In the request highlight 3&foo=3 right click and select "Fuzz...". Click the "Payloads" button, click the "Add" button, set the "Type" dropdown as "Script", select your "Script" by name in the dropdown (I called mine "Pitchfork"). ("Generate Preview" if you like.) Click the "Add" button. Click the "Ok" button. Click "Start Fuzzer". You've now run a "Pitchfork" fuzz in ZAP.
Results in the following payloads:
1&foo=1
2&foo=2
3&foo=3
4&foo=4
5&foo=5
6&foo=6
7&foo=7
8&foo=8
9&foo=9
10&foo=10
Things to keep in mind:
Assuming you're fuzzing a normal GET or POST you should be able to order the params however you like. (Targets "shouldn't" care which order params are in, you can copy/paste them into whatever order you need and send the request manually.) If it's some sort of well formed content (JSON/XML, or whatever) then you can just turn MID into a huge string...
You can install/use a scripting add-on such as Python (Jython) if you want to access payloads from a file.
If you wanted to process a header based on the same payload as the initial injection then you'd do a slight variation.
Create a "Fuzzer HTTP Processor" script, which is just a slight variation on the template. The following example simply checks the value of the payload in foo and uses it in a header:
/**
* Processes the fuzzed message (payloads already injected).
*
* Called before forwarding the message to the server.
*
* #param {HttpFuzzerTaskProcessorUtils} utils - A utility object that contains functions that ease common tasks.
* #param {HttpMessage} message - The fuzzed message, that will be forward to the server.
*/
function processMessage(utils, message) {
// To obtain the list of payloads:
// utils.getPayloads()
// To obtain original message:
// utils.getOriginalMessage()
// To stop fuzzer:
// utils.stopFuzzer()
// To increases the error count with a reason:
// utils.increaseErrorCount("Reason Error Message...")
// To send a message, following redirects:
// utils.sendMessage(myMessage)
// To send a message, not following redirects:
// utils.sendMessage(myMessage, false)
// To add a message previously sent to results:
// utils.addMessageToResults("Type Of Message", myMessage)
// To add a message previously sent to results, with custom state:
// utils.addMessageToResults("Type Of Message", myMessage, "Key Custom State", "Value Custom State")
// The states' value is shown in the column 'State' of fuzzer results tab
// To get the values of the parameters configured in the Add Message Processor Dialog.
// utils.getParameters()
// A map is returned, having as keys the parameters names (as returned by the getRequiredParamsNames()
// and getOptionalParamsNames() functions below)
// To get the value of a specific configured script parameter
// utils.getParameters().get("exampleParam1")
// Process fuzzed message...
var payload = null;
for (var iterator = message.getUrlParams().iterator(); iterator.hasNext();) {
var urlParam = iterator.next();
if (urlParam.getName() == 'foo') {
payload = urlParam.getValue();
break;
}
}
message.getRequestHeader().setHeader("X-Some-Id", payload);
}
/**
* Processes the fuzz result.
*
* Called after receiving the fuzzed message from the server.
*
* #param {HttpFuzzerTaskProcessorUtils} utils - A utility object that contains functions that ease common tasks.
* #param {HttpFuzzResult} fuzzResult - The result of sending the fuzzed message.
* #return {boolean} Whether the result should be accepted, or discarded and not shown.
*/
function processResult(utils, fuzzResult){
// All the above 'utils' functions are available plus:
// To raise an alert:
// utils.raiseAlert(risk, confidence, name, description)
// To obtain the fuzzed message, received from the server:
// fuzzResult.getHttpMessage()
// To get the values of the parameters configured in the Add Message Processor Dialog.
// utils.getParameters()
// A map is returned, having as keys the parameters names (as returned by the getRequiredParamsNames()
// and getOptionalParamsNames() functions below)
// To get the value of a specific configured script parameter
// utils.getParameters().get("exampleParam1")
return true;
}
/**
* This function is called during the script loading to obtain a list of the names of the required configuration parameters,
* that will be shown in the Add Message Processor Dialog for configuration. They can be used
* to input dynamic data into the script, from the user interface
*/
function getRequiredParamsNames(){
return [];
}
/**
* This function is called during the script loading to obtain a list of the names of the optional configuration parameters,
* that will be shown in the Add Message Processor Dialog for configuration. They can be used
* to input dynamic data into the script, from the user interface
*/
function getOptionalParamsNames(){
return [];
}
You'd select just the param value you want to fuzz. In the above example if you wanted to fuzz foo you'd just select the 3. Setup the fuzzer much as above (you could use a built-in generator instead of a script), but add your "Message Processor" in the "Message Processors" tab, run the fuzzer.
Based on this example foo should get the values 1 thru 10 and each request will have a header such as X-Some-Id: 1 added (where the Id is 1 to 10 kept in pace with the payload).
Of course you could also do a substring, encoding, etc. it doesn't have to be exactly the same.

Related

Nock fixtures: Ignore request body when matching

In the Readme for nock it explains how one can ignore the request body.
I am using the fixtures feature of nock and I need to ignore the request body (at least partially). How can I do this? Can I write a regex in the body field of the json entries in the fixtures files?
I solved it myself after some painful digging. Here is the solution:
I am trying to nock requests to the Kraken API. The request for the endpoint /0/private/TradesHistory is of method POST and sends a body containing a "query string like" string. For example the body will look like ofs=50&nonce=xxx. The nonce value changes with each request, so I want to ignore it to when looking for a matching nock. (The nonce can only be used once, it would make no sense for the client library to send the same value again).
So I have to add a "preprocessing" function as a config object to the call to nockBack like so:
import * as queryString from 'query-string'
import { back } from 'nock'
import * as path from 'path'
const before = (scope: any) => {
scope.filteringRequestBody = (body: string, aRecordedBody: string) => {
const { of: currentOffset, } = queryString.parse(`?${body}`) //Prepend a `?` so it is actually a query string.
const { of: recordedOffset, } = queryString.parse(`?${body}`)
if (!(currentOffset || recordedOffset)) {//This is the case where no offset is set. There is only possible recorded body in my case that matches this: The other body which has no offset. I replace the body with the recorded body to produce a match.
return aRecordedBody
}
if (currentOffset === recordedOffset) {//This is the case where the sent body has the same offset as the recorded body. I replace the body with the recorded body in order to produce a match.
return aRecordedBody
}
return body
}
}
back.fixtures = `${__dirname}/nockFixtures/`
const { nockDone } = back('nocks.json',{before})
...//Run my queris
nockDone()
Now it works like a charm.

YUIDOC - Document params of returned callback

How can I properly document a returned callback that returns multiple parameters. Exe:
return callback(error, success);
I would document this return value as follows:
/**
* #return {Function} Returns the callback in the form of `callback(error, success)`
*/
That is -- use the actual string description of the return statement. (YUIDoc doesn't really have the ability to document what you want explicitly, without describing in text.)

How can I update hasandbelongstomany relations for multiple models at once in strongloop loopback

I have 2 models in a Strongloop Loopback API
Products
Tags
Between those 2 models I have defined a hasAndBelongsToMany-relation.
In my CMS I want a bulk-update functionality for my products, in which I can select many Products, and assign many tags, in one action.
How can I save those easily to my Mysql-DB, without having to iterate over each product, then iterate over each tag, and link those 2 ?
I checked in the docs and found the add and remove functions, but those only connect one model to one relatedModel. Is there already loopback-functionality to do what I want?
I wrote a custom (updated) function along with a helper in a service:
/*
* Add multiple objects to a relationship
* #param {object} origin The object which hold the items
* #param {array} data The new list to be inserted
* #param {string} relation name of the relationship, for instance 'cats'
*/
exports.updateManyRelations = function(origin, data, relation){
//Destroy all old connections
return origin[relation].destroyAll().then(function(response){
//All were deleted and nothing to add
if(!data || data.length == 0){return [];}
//We have a list to go through, do the dirty work!
return addMultipleRelationsToObject(origin[relation], data, []);
}).then(function(newList){
// new items created
return newList
}, function(error){
console.log(error);
});
}
/*
* Helper function to add multiple related objects to a object in a correct promise chain
*/
var addMultipleRelationsToObject = function(objectRelation, list, newList){
if(list && list.length == 0){return Promise.resolve(newList);}
var item = list.pop();
if(!objectRelation.hasOwnProperty("add")){
return Promise.reject("Relationship is wrong for: " + objectRelation);
}
return objectRelation.add(item.id).then(function(newlyAdded){
newList.push(item);
return addMultipleRelationsToObject(objectRelation, list, newList);
});
}
Unfortunately, there is no bulk update yet. See https://github.com/strongloop/loopback/issues/1275

DELETE methods not callable

I don't seem to be able to call DELETE methods via Restler for some reason. In my restler php file I've defined a method like so:
/**
* Drop invitation
*
* Removes an invitation from the system. Coaches want a way to be able
* to remove an invite if they messed up, or the person just doesn't accept.
*
* #param string $email The email address of the invited person {#from body}
* #param int $team_id The SQL identifier for the team {#from body}
*
* #return array An empty array
*/
public function deleteInvite($email, $team_id) {
return [];
}
When I try to call it:
curl -X DELETE -H "Content-Type: application/json" -d '{"email":"foo","team_id",17}' http://server.com/app/team/invite
It comes back with a 400 error:
{
"error": {
"code": 400,
"message": "Bad Request: email is missing."
}
}
How am I supposed to be calling this?
as a widely accepted practice restler does not support/read body parameters for GET and DELETE requests. Send them on url or as a query string instead.
As you can see in https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-p2-semantics-26#section-4.3
HEAD - No defined body semantics.
GET - No defined body semantics.
PUT - Body supported.
POST - Body supported.
DELETE - No defined body semantics.
TRACE - Body not supported.
OPTIONS - Body supported but no semantics (maybe in the future).
Many http servers do not parse the body of GET and DELETE requests, restler is following that practice

Yii: shared password for member's area

What would be the best way to approach setting up a shared password protected area in Yii?
I am looking to have a view of a Group model, that can be accessed by a shared password created by the owner of that group - group members shouldn't have to log in, purely enter this passcode.
Should this still be done with Yii's built in auth tools? - or is there a simpler solution, bearing in mind that someone might want to access several groups.
You can do this using standard session mechanism built into PHP. When someone tries to view password-protected area, check the session variable, if the user haven't entered password yet then redirect him to some page with a password form (you can do the check using controller filters for example).
After the form is submitted, check correctness of password and if everything is ok, write it into the session. You can differentiate session keys by group ids.
You can use Yii filter capabilities to fire code before executing a controller action, and prevent actions that you do not want to allow.
I would create a common controller for all your group pages, and inherit other controller from this one if need to.
In the filter I would setup code to check/prompt for the password, and keep that in session.
For example we have a filter setup to detect if the user has accepted our revised Terms and Conditions. The filter will detect and will prevent access to the controller until the user doesn't confirm it.
class TocConfirmFilter extends CFilter {
/**
* Initializes the filter.
* This method is invoked after the filter properties are initialized
* and before {#link preFilter} is called.
* You may override this method to include some initialization logic.
*/
public function init() {
}
/**
* Performs the pre-action filtering.
* #param CFilterChain the filter chain that the filter is on.
* #return boolean whether the filtering process should continue and the action
* should be executed.
*/
protected function preFilter($filterChain) {
// do not perform this filter on this action
if ($filterChain->action->controller->id . '/' . $filterChain->action->id == 'public/onePublicPage') {
return true;
}
if (isset(Yii::app()->user->id)) {
$user = user::model()->findbyPk(Yii::app()->user->id);
if ($user === null)
throw new CHttpException(404, 'The requested user does not exist.');
if ($user->tocconfirmed == 0) {
Yii::app()->getRequest()->redirect(Yii::app()->createAbsoluteUrl('authorize/confirm'));
return false;
}
}
return true;
}
/**
* Performs the post-action filtering.
* #param CFilterChain the filter chain that the filter is on.
*/
protected function postFilter($filterChain) {
}
}