How to create chrome crx file programmatically (preferably in java)? - command-line

I want to create chrome extension crx file programatically (not using chrome.exe, because it opens new chrome window). So what are the alternatives for same ? My preference is java, but if its possible in other language then also I am okay.

As kylehuff stated, there are external tools that you could use. But you can always use the command line from Google Chrome to do that which is cross platform (Linux / Windows / Mac).
chrome.exe --pack-extension=[extension_path] --pack-extension-key=[extension_key]
--pack-extension is:
Package an extension to a .crx installable file from a given directory.
--pack-extension-key is:
Optional PEM private key is to use in signing packaged .crx.
The above does not run Google Chrome, it is just command line packing using Chromium's core crx algorithm that they use internally.

There is a variety of utilities to do this, in various languages (albeit; they are mostly shell/scripting languages)
I cannot post the links to all of them, because I am a new stackoverflow user - I can only post 1 link, so I created a page which lists them all - including the one C one I speak about below - http://curetheitch.com/projects/buildcrx/6/
Anyway, I spent a few hours and put together a version in C which runs on Windows or Linux, as the other solutions require installation of a scripting language or shell (i.e. python, ruby, bash, etc.) and OpenSSL. The utility I wrote has OpenSSL statically linked so there are no interpreter or library requirements.
The repository is hosted on github, but the link above has a list of my utility and other peoples solutions.
Nothing listed for Java, which was your preference, but hopefully that helps!

//Method to generate .crx. signature
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Signature;
//#param : extenstionContents is your zip file ,
//#returns : byte[] of the signature , use ByteBuffer to merge them and you have your
// .crx
public static byte[] generateCrxHeader(byte[] extensionContents) throws Exception {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
SecureRandom random = new SecureRandom();
keyGen.initialize(1024, random);
KeyPair pair = keyGen.generateKeyPair();
Signature sigInstance = Signature.getInstance("SHA1withRSA");
sigInstance.initSign(pair.getPrivate());
sigInstance.update(extensionContents);
byte [] signature = sigInstance.sign();
byte [] subjectPublicKeyInfo = pair.getPublic().getEncoded();
final int headerLength = 4 + 4 + 4 + 4 + subjectPublicKeyInfo.length + signature.length;
ByteBuffer headerBuf = ByteBuffer.allocate(headerLength);
headerBuf.order(ByteOrder.LITTLE_ENDIAN);
headerBuf.put(new byte[]{0x43,0x72,0x32,0x34}); // Magic number
headerBuf.putInt(2); // Version
headerBuf.putInt(subjectPublicKeyInfo.length); // public key length
headerBuf.putInt(signature.length); // signature length
headerBuf.put(subjectPublicKeyInfo);
headerBuf.put(signature);
final byte [] header = headerBuf.array();
return header;
}

I needed to do this in Ruby. JavaHead's answer looks nice for Java for CRX2. The current format is CRX v3 and header is protobuf based. I wrote a blog for packing an extension with Ruby. There is also a python project from another author.
I'll paste Ruby version of CRX2 and CRX3 methods for packing extensions for a reference here. For complete code see my blog.
So CRX3 method:
def self.header_v3_extension(zipdata, key: nil)
key ||= OpenSSL::PKey::RSA.generate(2048)
digest = OpenSSL::Digest.new('sha256')
signed_data = Crx_file::SignedData.new
signed_data.crx_id = digest.digest(key.public_key.to_der)[0...16]
signed_data = signed_data.encode
signature_data = String.new(encoding: "ASCII-8BIT")
signature_data << "CRX3 SignedData\00"
signature_data << [ signed_data.size ].pack("V")
signature_data << signed_data
signature_data << zipdata
signature = key.sign(digest, signature_data)
proof = Crx_file::AsymmetricKeyProof.new
proof.public_key = key.public_key.to_der
proof.signature = signature
header_struct = Crx_file::CrxFileHeader.new
header_struct.sha256_with_rsa = [proof]
header_struct.signed_header_data = signed_data
header_struct = header_struct.encode
header = String.new(encoding: "ASCII-8BIT")
header << "Cr24"
header << [ 3 ].pack("V") # version
header << [ header_struct.size ].pack("V")
header << header_struct
return header
end
And for historic purposes (this one verified) CRX2:
# #note original crx2 format description https://web.archive.org/web/20180114090616/https://developer.chrome.com/extensions/crx
def self.header_v2_extension(zipdata, key: nil)
key ||= OpenSSL::PKey::RSA.generate(2048)
digest = OpenSSL::Digest.new('sha1')
header = String.new(encoding: "ASCII-8BIT")
signature = key.sign(digest, zipdata)
signature_length = signature.length
pubkey_length = key.public_key.to_der.length
header << "Cr24"
header << [ 2 ].pack("V") # version
header << [ pubkey_length ].pack("V")
header << [ signature_length ].pack("V")
header << key.public_key.to_der
header << signature
return header
end
I have used the excellent service crx-checker to validate both - v2 and v3 extension packing. Where I'm getting the expected RSASSA-PKCS1-v1_5 signature marked (Signature OK) (Developer Signature).
The extension will fail to load with CRX_REQUIRED_PROOF_MISSING if you try to add to your browser from URL because it will be lacking Google signature. But it will be loaded fine by Selenium when running test. To load normally you need to publish on web store.

Related

How can I can list of alerts associated with scan rules in OWASP ZAP?

I want to get the list of alerts in a tabular form like below. I copy the URL's in the alerts and manually prepare such a tabular table myself. However, I need to do this automatically or semi-automatically (at least)
Alert Name URL Scan Type Scan_Name WASCID CWEID
---------- --------------- --------- --------- ----- ------
You can export the report in XML and apply any kind of XSL transform to it that you might like.
You could pull the XML report into Excel (or whatever spreadsheet program) and manipulate it.
You could pull alerts from the web API and have them in XML or json and process them however you like programmatically.
You could write a standalone script (within ZAP) to traverse the Alerts tree and output the details tab delimited in the script console pane. For example:
extAlert = org.parosproxy.paros.control.Control.getSingleton().
getExtensionLoader().getExtension(
org.zaproxy.zap.extension.alert.ExtensionAlert.NAME)
extPscan = org.parosproxy.paros.control.Control.getSingleton().
getExtensionLoader().getExtension(
org.zaproxy.zap.extension.pscan.ExtensionPassiveScan.NAME);
var pf = Java.type("org.parosproxy.paros.core.scanner.PluginFactory");
printHeaders();
if (extAlert != null) {
var Alert = org.parosproxy.paros.core.scanner.Alert;
var alerts = extAlert.getAllAlerts();
for (var i = 0; i < alerts.length; i++) {
var alert = alerts[i]
printAlert(alert);
}
}
function printHeaders() {
print('AlertName\tSource:PluginName\tWASC\tCWE');
}
function printAlert(alert) {
var scanner = '';
// If the session is loaded in ZAP and one of the extensions that provided a plugin for the
// existing alerts is missing (ex. uninstalled) then plugin (below) will be null, and hence scanner will end-up being empty
if (alert.getSource() == Alert.Source.ACTIVE) {
plugin = pf.getLoadedPlugin(alert.getPluginId());
if (plugin != null) {
scanner = plugin.getName();
}
}
if (alert.getSource() == Alert.Source.PASSIVE && extPscan != null) {
plugin = extPscan.getPluginPassiveScanner(alert.getPluginId());
if (plugin != null) {
scanner = plugin.getName();
}
}
print(alert.getName() + '\t' + alert.getSource() + ':' + scanner + '\t' + alert.getWascId() + '\t' + alert.getCweId());
// For more alert properties see https://static.javadoc.io/org.zaproxy/zap/2.7.0/org/parosproxy/paros/core/scanner/Alert.html
}
Produces script console output like (note the 2nd, 6th, and 7th rows the specific alert name differs from the general scanner name):
Alert_Name Source:PluginName WASC CWE
Cross Site Scripting (DOM Based) ACTIVE:Cross Site Scripting (DOM Based) 8 79
Non-Storable Content PASSIVE:Content Cacheability 13 524
Content Security Policy (CSP) Header Not Set PASSIVE:Content Security Policy (CSP) Header Not Set 15 16
Server Leaks Version Information via "Server" HTTP Response Header Field PASSIVE:HTTP Server Response Header Scanner 13 200
Server Leaks Information via "X-Powered-By" HTTP Response Header Field(s) PASSIVE:Server Leaks Information via "X-Powered-By" HTTP Response Header Field(s) 13 200
Non-Storable Content PASSIVE:Content Cacheability 13 524
Timestamp Disclosure - Unix PASSIVE:Timestamp Disclosure 13 200
Which pastes well in Excel:
Detailed steps:
(This assumes ZAP is running, and the session you want information for is open/loaded).
1. Goto the scripts tree (behind the Sites Tree) [if you can't see it
click the plus sign near the Sites Tree tab and add "Scripts"].
2. In the Scripts tree right click "Standalone" and select "New Script":
give it a name and select the JavaScript Script Engine ("EcmaScript
: Oracle Nashorn") [no Template is necessary]. Click "Save" on the New
Script dialog.
3. In the new script window (in the request/response area) paste the script
from the answer.
4. Run it (the blue triangle play button above the script
entry pane).
5. The results will display in the output pane below the
script.
6. Copy/paste the output into Excel.

WinVerifyTrust returns CERT_E_UNTRUSTEDROOT for a valid (loaded) driver

In the following code snippet, WinVerifyTrust returns CERT_E_UNTRUSTEDROOT for a kernel driver file (.sys) that is loaded and running on the system:
GUID guidAction = DRIVER_ACTION_VERIFY;
WINTRUST_FILE_INFO sWintrustFileInfo = { 0 };
WINTRUST_DATA sWintrustData = { 0 };
HRESULT hr = 0;
sWintrustFileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO);
sWintrustFileInfo.pcwszFilePath = argv[1];
sWintrustFileInfo.hFile = NULL;
sWintrustData.cbStruct = sizeof(WINTRUST_DATA);
sWintrustData.dwUIChoice = WTD_UI_NONE;
sWintrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
sWintrustData.dwUnionChoice = WTD_CHOICE_FILE;
sWintrustData.pFile = &sWintrustFileInfo;
sWintrustData.dwStateAction = WTD_STATEACTION_VERIFY;
hr = WinVerifyTrust((HWND)INVALID_HANDLE_VALUE, &guidAction, &sWintrustData);
A few interesting points:
- The driver is signed with a valid (purchased) certificate using SHA-256.
- KB3033929 is installed on the system (Win7/32)
- When viewing the certificate from the file properties, the entire certification chain shows up as valid
Am I calling WinVerifyTrust wrong?
Alternative question: is there another way of knowing (by the presence of a registry key or something similar) that SHA-256 based code signing verification is available on the target system? (I need to verify this during installation...)
Thanks :)
DRIVER_ACTION works good for WHQL afaik. Try
GUID WINTRUST_ACTION_GENERIC_VERIFY_V2
Here is something else you can refer to
http://gnomicbits.blogspot.in/2016/03/how-to-verify-pe-digital-signature.html

Generating Content-MD5 for AWS S3 REST in Haxe

I'm trying to add a Content-MD5 header to my REST calls to AWS S3 in Haxe (compiling to PHP). It's generated by
var contentMD5 = haxe.crypto.Base64.encode(haxe.io.Bytes.ofString(haxe.crypto.Md5.encode(_data)));
with _data in my example being
<Delete><Object><Key>nathan/storage/72ENgrtnpA5VAoy7zEpzPRNEChN0TRGc</Key></Object><Object><Key>nathan/storage/7rlZZSJFvZ7AxUhQZsh4ufn9M2x8m1ae</Key></Object><Object><Key>nathan/storage/HN8NFlUnJiiGo7qlddvRrlGE6hPmWMnZ</Key></Object><Object><Key>nathan/storage/SFsZ8z63DswEVFJQJqmUwbenaWyfZ8zb</Key></Object><Object><Key>nathan/storage/YSYXXgYbSZixOKo27PL65ii6nCeiFesl</Key></Object></Delete>
My full request sent to AWS (for a multiple delete call as described here http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html), using AWS signature version 4 (bucket, signature, and credential shortened):
POST /?delete= HTTP/1.1
Host: mybucket.s3-eu-central-1.amazonaws.com
Content-Length: 392
x-amz-content-sha256: 53da469cb6fc9d0701a1c6ff98d48edd361cd8a90d8a290a2dd224b2681bf7fb
x-amz-date: 20150923T195117Z
Authorization: AWS4-HMAC-SHA256 Credential=zzzzz/20150923/eu-central-1/s3/aws4_request, SignedHeaders=content-md5;host;x-amz-date, Signature=xxxxx
Content-MD5: MDAzNDZmZjJiMGJkMDFkNzVjYzFiOGE4MzI5NTc0NGY=
<Delete><Object><Key>nathan/storage/72ENgrtnpA5VAoy7zEpzPRNEChN0TRGc</Key></Object><Object><Key>nathan/storage/7rlZZSJFvZ7AxUhQZsh4ufn9M2x8m1ae</Key></Object><Object><Key>nathan/storage/HN8NFlUnJiiGo7qlddvRrlGE6hPmWMnZ</Key></Object><Object><Key>nathan/storage/SFsZ8z63DswEVFJQJqmUwbenaWyfZ8zb</Key></Object><Object><Key>nathan/storage/YSYXXgYbSZixOKo27PL65ii6nCeiFesl</Key></Object></Delete>
The response is as follows (RequestId and HostId shortened)
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>InvalidDigest</Code><Message>The Content-MD5 you specified was invalid.</Message><Content-MD5>MDAzNDZmZjJiMGJkMDFkNzVjYzFiOGE4MzI5NTc0NGY=</Content-MD5><RequestId>rrrrrr</RequestId><HostId>ccccccc</HostId></Error>
In my opinion, the generated MD5 is correct. I verified the value with other tools. Also, note that x-amz-content-sha256 is based on the same _data and AWS accepted that header in my previous (non delete) calls.
What am I missing here? Why does my MD5 value differ from the one AWS generates?
You're very close.
Here's the problem:
An md5 hash is 16 bytes in binary representation, 32 characters in hexadecimal representation, and 24 characters (Including padding) in base64.
Yours is approximately twice as long. You appear to be taking the 32 character hex md5, and base64-encoding that, resulting in a base64 string of about 44 characters, instead of just encoding the binary form.
Note that the length of the output, 44 vs 24 is not a factor of two in spite of my assertion that you are encoding 32 initial bytes instead of 16. That's expected, because base64 output_bytes = ceil(input_bytes/3) * 4.
My expertise is with the S3 API -- not haxe, which I've never used -- so the above is almost certainly correct, but the following is wild speculation.
var contentMD5 = haxe.crypto.Base64.encode(haxe.crypto.Md5.make(_data));
Any one out there looking to do this in java can use this code
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import org.apache.commons.codec.binary.Base64;
public class GenerateMD5 {
public static void main(String args[]) throws Exception{
String s = "<CORSConfiguration> <CORSRule> <AllowedOrigin>http://www.example.com</AllowedOrigin> <AllowedMethod>PUT</AllowedMethod> <AllowedMethod>POST</AllowedMethod> <AllowedMethod>DELETE</AllowedMethod> <AllowedHeader>*</AllowedHeader> <MaxAgeSeconds>3000</MaxAgeSeconds> </CORSRule> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedHeader>*</AllowedHeader> <MaxAgeSeconds>3000</MaxAgeSeconds> </CORSRule> </CORSConfiguration>";
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(s.getBytes());
byte[] digest = md.digest();
StringBuffer sb = new StringBuffer();
/*for (byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}*/
System.out.println(sb.toString());
StringBuffer sbi = new StringBuffer();
byte [] bytes = Base64.encodeBase64(digest);
String finalString = new String(bytes);
System.out.println(finalString);
}
}
The commented code is where most people get it wrong changing it to hex

KRL: Signing requests with HMAC_SHA1

I made a test suite for math:hmac_* KRL functions. I compare the KRL results with Python results. KRL gives me different results.
code: https://gist.github.com/980788 results: http://ktest.heroku.com/a421x68
How can I get valid signatures from KRL? I'm assuming that they Python results are correct.
UPDATE: It works fine unless you want newline characters in the message. How do I sign a string that includes newline characters?
I suspect that your python SHA library returns a different encoding than is expected by the b64encode library. My library does both the SHA and base64 in one call so I to do some extra work to check the results.
As you show in your KRL, the correct syntax is:
math:hmac_sha1_base64(raw_string,key);
math:hmac_sha256_base64(raw_string,key);
These use the same libraries that I use for the Amazon module which is testing fine right now.
To test those routines specifically, I used the test vectors from the RFC (sha1, sha256). We don't support Hexadecimal natively, so I wasn't able to use all of the test vectors, but I was able to use a simple one:
HMAC SHA1
test_case = 2
key = "Jefe"
key_len = 4
data = "what do ya want for nothing?"
data_len = 28
digest = 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79
HMAC SHA256
Key = 4a656665 ("Jefe")
Data = 7768617420646f2079612077616e7420666f72206e6f7468696e673f ("what do ya want for nothing?")
HMAC-SHA-256 = 5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
Here is my code:
global {
raw_string = "what do ya want for nothing?";
mkey = "Jefe";
}
rule first_rule {
select when pageview ".*" setting ()
pre {
hmac_sha1 = math:hmac_sha1_hex(raw_string,mkey);
hmac_sha1_64 = math:hmac_sha1_base64(raw_string,mkey);
bhs256c = math:hmac_sha256_hex(raw_string,mkey);
bhs256c64 = math:hmac_sha256_base64(raw_string,mkey);
}
{
notify("HMAC sha1", "#{hmac_sha1}") with sticky = true;
notify("hmac sha1 base 64", "#{hmac_sha1_64}") with sticky = true;
notify("hmac sha256", "#{bhs256c}") with sticky = true;
notify("hmac sha256 base 64", "#{bhs256c64}") with sticky = true;
}
}
var hmac_sha1 = 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79';
var hmac_sha1_64 = '7/zfauXrL6LSdBbV8YTfnCWafHk';
var bhs256c = '5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843';
var bhs256c64 = 'W9zBRr9gdU5qBCQmCJV1x1oAPwidJzmDnexYuWTsOEM';
The HEX results for SHA1 and SHA256 match the test vectors of the simple case.
I tested the base64 results by decoding the HEX results and putting them through the base64 encoder here
My results were:
7/zfauXrL6LSdBbV8YTfnCWafHk=
W9zBRr9gdU5qBCQmCJV1x1oAPwidJzmDnexYuWTsOEM=
Which match my calculations for HMAC SHA1 base64 and HMAC SHA256 base64 respectively.
If you are still having problems, could you provide me the base64 and SHA results from python separately so I can identify the disconnect?

Custom clipboard data format accross RDC (.NET)

I'm trying to copy a custom object from a RDC window into host (my local) machine. It fails.
Here's the code that i'm using to 1) copy and 2) paste:
1) Remote (client running on Windows XP accessed via RDC):
//copy entry
IDataObject ido = new DataObject();
XmlSerializer x = new XmlSerializer(typeof(EntryForClipboard));
StringWriter sw = new StringWriter();
x.Serialize(sw, new EntryForClipboard(entry));
ido.SetData(typeof(EntryForClipboard).FullName, sw.ToString());
Clipboard.SetDataObject(ido, true);
2) Local (client running on local Windows XP x64 workstation):
//paste entry
IDataObject ido = Clipboard.GetDataObject();
DataFormats.Format cdf = DataFormats.GetFormat(typeof(EntryForClipboard).FullName);
if (ido.GetDataPresent(cdf.Name)) //<- this always returns false
{
//can never get here!
XmlSerializer x = new XmlSerializer(typeof(EntryForClipboard));
string xml = (string)ido.GetData(cdf.Name);
StringReader sr = new StringReader(xml);
EntryForClipboard data = (EntryForClipboard)x.Deserialize(sr);
}
It works perfectly on the same machine though.
Any hints?
There are a couple of things you could look into:
Are you sure the serialization of the object truely converts it into XML? Perhaps the outputted XML have references to your memory space? Try looking at the text of the XML to see.
If you really have a serialized XML version of the object, why not store the value as plain-vanilla text and not using typeof(EntryForClipboard) ? Something like:
XmlSerializer x = new XmlSerializer(typeof(EntryForClipboard));
StringWriter sw = new StringWriter();
x.Serialize(sw, new EntryForClipboard(entry));
Clipboard.SetText(sw.ToString(), TextDataFormat.UnicodeText);
And then, all you'd have to do in the client-program is check if the text in the clipboard can be de-serialized back into your object.
Ok, found what the issue was.
Custom format names get truncated to 16 characters when copying over RDC using custom format.
In the line
ido.SetData(typeof(EntryForClipboard).FullName, sw.ToString());
the format name was quite long.
When i was receiving the copied data on the host machine the formats available had my custom format, but truncated to 16 characters.
IDataObject ido = Clipboard.GetDataObject();
ido.GetFormats(); //used to see available formats.
So i just used a shorter format name:
//to copy
ido.SetData("MyFormat", sw.ToString());
...
//to paste
DataFormats.Format cdf = DataFormats.GetFormat("MyFormat");
if (ido.GetDataPresent(cdf.Name)) {
//this not works
...