Oddtale – Writing with strangers

So to take a break in my writing, I decided to start a new project. It was first supposed to be an Easter project, but it turned out to take a lot more time than I first thought. It’s a game called “Oddtale” where anyone can register with their name and email (no password needed, yet) and write a chapter in the current story. Authors are picked at random from the current pool of registered users, and the one who gets picked can write as little or as much (there are limitations, but they are big enough to write an okay chapter) as they want. Each author, except the first one, gets a hint from the previous chapter, to make things a little easier.

One story has ten chapters, and when the current story finishes, it gets automatically published on the site for everyone to view. A few hours later, the next story starts and people can join and write on that one as well. I’m planning more advanced features like private/public writing groups for people who don’t want to wait to get picked in the main one, as well as more advanced writing tools like a wysiwyg editor with the support for bbcode tags. There are more features planned, but these two are the biggest ones that are currently in the works. I wont publish a new version for a while though as I want to see if the main idea with the site in its current state, can draw any attention. The game is pretty pointless if no one wants to play.

The address for the game is: https://oddtale.net/ (REMOVED)

Oh, and we also have a mascot (who you will see a lot more of if the game gets an active player base). The mascot was made by Nicole (Bunnico) on Fiverr.

Uploading dangers

A few weeks ago I was reading a forum thread about file upload scripts in the PHP scripting language. The people in the thread were discussing different ways of handling different file types when allowing users on their websites to upload files to the server. Security wasn’t really on the topic here, but there were still mentions of it. The most common problem that is mentioned when it comes to file uploading is that there is a need to somehow restrict what kind of files the users are allowed to upload, how to handle them once they are on the server and so on.

In this case their solution was to disallow users from uploading files with the php extension, and then by using a PHP function called ImageJpeg they would verify that the users were uploading valid pictures (The forum user in question was making an image upload script for his community website). Now, as a developer I can see why this seems like a pretty nice idea, since the data would be verified and changed in the ImageJpeg function. If the file was not a valid image then the function would return false and the file would not be properly uploaded. And even if a malicious user were to put code within the data part of the image, that data would always be changed when the ImageJpeg function has finished and saved the file to disc.

The so called “black listing” of file extensions is usually not a good idea since there exists many different alternatives to one executable extension. If we take the example above where they prevented users from uploading files with the php extension. PHP has 5 alternative extensions, these being .php3, .php4, .php5, .phtml and .phps. And if a developer of a script only restricts .php, then the other 5 can be used instead to upload malicious code to the server.

I found it an interesting topic and decided to see if I can somehow bypass their protection and upload executable code to my test server using their upload scripts. The first thing that came to mind was that jpeg images allow so called “exif” data that can hold comments for image viewers and editors to display in different ways. Although this bubble burst rather quickly as I discovered that the ImageJpeg function always overwrites the exif data with its own, including the comments.

So now I had to fire up a hex editor and get to work, and see if I can insert data into the image itself, while making sure it’s still a valid image that would pass through the ImageJpeg function. The difficulty with this, just as discussed in the thread, was that the data was always changed and thus my code was not intact when the file was saved to disc. The image would in almost all cases be a valid one, although a bit distorted due to my meddling.

After hours of playing around with this I managed to get an image that when injected with code and run through the function, the code would still be intact and executable on my server.

hex

So after even more hours of trying to perfect this method, making sure the image is always a valid one and that it’s not too distorted from the changes, I wrote a script that injects the code automatically and makes sure that the code will still be there after being changed with different image handling tools, like the ImageJpeg function (It was also tested with tools that performs resize on the picture, and although this worked in many cases it was significantly harder to retain the code after processing).

Below is the picture before the injection

logo

Followed by the picture after the injection (notice how in this example, the picture got a little bit distorted at the end. This varies from case to case). Don’t worry, the code can’t execute in its current form.

logo_mod

So to summarize. After a few days I did manage to bypass their protection followed by writing a script to automate it all. Some example output from the script can be seen below.


[+] Jumping to end byte
[+] Searching for valid injection point
[+] Injection completed successfully
[+] Filename: result.phtml

And to top it up I also made a small script to send commands to the file once it has been uploaded to a server, parse the results out of the image data once returned and display it.


uname -a
Linux truesechp01 3.13.0-29-generic #53-Ubuntu SMP Wed Jun 4 21:00:20 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

And this shows how very easy it is for things to go wrong with file upload functionality. There are so very many ways to do bad things with it, and a motivated attacker can in many cases spend days, months or even years to find a way around your protection mechanisms. The trick I have showed you in this article today is not limited to PHP, but can be applied in other environments as well. There are a few measures that can be good enough depending on the situation when handling uploaded files to a server, but there’s no silver bullet as there are in many cases ways to circumvent them as well, the best example being that the developer who implements it simply does it wrongly (classic one is that they only check for the presence of .jpg in a filename, which would then allow the uploading of a file.jpg.php).

For those who decide to solve all this by using blacklisting and block file extensions like .php,.phtml,.php4,.php4,.php5. Be aware, PHP 7 will be released eventually 😉

UPDATE: And PHP 7 is here, and the vulnerability is back 🙂

Wrote an EAN13 barcode program

I decided to write a barcode generator in Java “for no apparent reason”, that can currently generator EAN13 barcodes. It’s available here in this post, but if you want the latest code then I suggest you check it out on github.

Github repo:
https://github.com/jra89/barcode

Usage:

java -jar barcode.jar numbers path barwidth barheight

Example:

java -jar barcode.jar 999990000020 /home/user/Desktop/EAN_IMG.png 2 60

Main.java

package barcode;

public class Main
{
	public static void main(String[] args)
	{
		String code;
		String path;
		int barWidth;
		int barHeight;

		if(args.length <= 1)
		{
			System.out.println("Not enough parameters");
		}
		
		try
		{
			code = args[0]; 
		}
		catch(NullPointerException e)
		{
			code = "9999900000207";
		}
		
		try
		{
			path = args[1];
		}
		catch(NullPointerException e)
		{
			path = "/tmp/barcode.png";
		}
		
		try
		{
			barWidth = Integer.parseInt(args[2]);
		}
		catch(NullPointerException e)
		{
			barWidth = 1;
		}
		
		try
		{
			barHeight = Integer.parseInt(args[3]);
		}
		catch(NullPointerException e)
		{
			barHeight = 100;
		}

		Ean13 ean = new Ean13(code, path, barWidth, barHeight);
		ean.createBarcodePNG();

	}
}

Ean13.java

package barcode;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Ean13 
{
	/**
	 * -=Structure=-
	 * 
	 * Fist, First Six, Last Six
	 * LGR = 012
	 * 0 	LLLLLL 	RRRRRR
	 * 1 	LLGLGG 	RRRRRR
	 * 2 	LLGGLG 	RRRRRR
	 * 3 	LLGGGL 	RRRRRR
	 * 4 	LGLLGG 	RRRRRR
	 * 5 	LGGLLG 	RRRRRR
	 * 6 	LGGGLL 	RRRRRR
	 * 7 	LGLGLG 	RRRRRR
	 * 8 	LGLGGL 	RRRRRR
	 * 9 	LGGLGL 	RRRRRR
	 *
	 * -=Encodings=-
	 *
	 * Digit 	L-code 	G-code 	R-code
	 * 0 	0001101 	0100111 	1110010
	 * 1 	0011001 	0110011 	1100110
	 * 2 	0010011 	0011011 	1101100
	 * 3 	0111101 	0100001 	1000010
	 * 4 	0100011 	0011101 	1011100
	 * 5 	0110001 	0111001 	1001110
	 * 6 	0101111 	0000101 	1010000
	 * 7 	0111011 	0010001 	1000100
	 * 8 	0110111 	0001001 	1001000
	 * 9 	0001011 	0010111 	1110100
	**/
	
	private String code;
	private String path;
	private BufferedImage bi;
    private Graphics2D ig2;
    private int barPos;
    private int barPosBin;
    private int imgPixelPos;
    private int barWidth;
	private int barHeight;
	private int imgWidth;
	private int imgHeight;
    private int[][] barcodeBinary;
	
	private int[][] firstSix = {
			{0,0,0,0,0,0}, //LLLLLL
			{0,0,1,0,1,1}, //LLGLGG
			{0,0,1,1,0,1}, //LLGGLG
			{0,0,1,1,1,0}, //LLGGGL
			{0,1,0,0,1,1}, //LGLLGG
			{0,1,1,0,0,1}, //LGGLLG
			{0,1,1,1,0,0}, //LGGGLL
			{0,1,0,1,0,1}, //LGLGLG
			{0,1,0,1,1,0}, //LGLGGL
			{0,1,1,0,1,0}  //LGGLGL
	};
	
	private int[] lastSix = {2, 2, 2, 2, 2, 2};
	
	private int[][][] encodings = {
			{
				{0,0,0,1,1,0,1}, 
				{0,1,0,0,1,1,1}, 
				{1,1,1,0,0,1,0}
			},
			{
				{0,0,1,1,0,0,1},
				{0,1,1,0,0,1,1},
				{1,1,0,0,1,1,0}
			},
			{
				{0,0,1,0,0,1,1},
				{0,0,1,1,0,1,1}, 	
				{1,1,0,1,1,0,0}
			},
			{
				{0,1,1,1,1,0,1},
				{0,1,0,0,0,0,1},
				{1,0,0,0,0,1,0}
			},
			{
				{0,1,0,0,0,1,1},
				{0,0,1,1,1,0,1},
				{1,0,1,1,1,0,0}
			},
			{
				{0,1,1,0,0,0,1},
				{0,1,1,1,0,0,1},
				{1,0,0,1,1,1,0}
			},
			{
				{0,1,0,1,1,1,1},
				{0,0,0,0,1,0,1},
				{1,0,1,0,0,0,0}
			},
			{
				{0,1,1,1,0,1,1},
				{0,0,1,0,0,0,1},
				{1,0,0,0,1,0,0}
			},
			{
				{0,1,1,0,1,1,1},
				{0,0,0,1,0,0,1},
				{1,0,0,1,0,0,0}
			},
			{
				{0,0,0,1,0,1,1},
				{0,0,1,0,1,1,1},
				{1,1,1,0,1,0,0}
			}
	};
	
	Ean13(String code, String path,int barWidth, int barHeight)
	{
		this.code = code;
		this.path = path;
		this.barPos = 12;
		this.barPosBin = 7;
		this.imgPixelPos = 0;
		this.barcodeBinary = new int[barPos][barPosBin];
		this.barWidth = barWidth;
		this.barHeight = barHeight;
		this.imgWidth = ((12*7) + (2*9) + (2*3) + (1*5)) * this.barWidth;
		this.imgHeight = this.barHeight;
	}
    
	public void createBarcodePNG()
	{
		this.code += Integer.toString(calculateControlDigit(this.code));
		generateBinaryMap();
		generateBarcodePNG();
	}
	
	private void generateBinaryMap()
	{
		int first = Integer.parseInt(String.valueOf(this.code.charAt(0)));
				
		//i = 1, first digit is not welcome to the bar
		for(int i = 1; i < 13; ++i)
		{
			int current = Integer.parseInt(String.valueOf(this.code.charAt(i)));
			
			if(i < 7)
				this.barcodeBinary[i-1] = this.encodings[current][this.firstSix[first][i-1]];
			else
				this.barcodeBinary[i-1] = this.encodings[current][this.lastSix[i-7]];
		}
	}
	
	private void generateBarcodePNG()
	{
		try {
		      // TYPE_INT_ARGB specifies the image format: 8-bit RGBA packed
		      // into integer pixels
		      bi = new BufferedImage(this.imgWidth, this.imgHeight, BufferedImage.TYPE_INT_ARGB);
		      ig2 = bi.createGraphics();
		      
		      ig2.setPaint(Color.white);
		      ig2.fillRect ( 0, 0, bi.getWidth(), bi.getHeight() );

		      //Draw quiet zone
		      drawSpecial(0);
		      
		      //Draw lead
		      drawSpecial(1);
		      
		      //Draw first group
		      drawGroup(1);
		      
		      //Draw separator
		      drawSpecial(2);
		      
		      //Draw second group
		      drawGroup(2);
		      
		      //Draw lead
		      drawSpecial(1);
		      
		      //Draw quiet zone
		      drawSpecial(0);

		      ImageIO.write(bi, "PNG", new File(path));
		      
		    } catch (IOException ie) {
		      ie.printStackTrace();
		    }
	}
	
	private void drawGroup(int groupPart)
	{
		int i = 0, length = 0;
		if(groupPart == 1)
		{
			i = 0;
			length = (this.barcodeBinary.length/2);
		}
		else if(groupPart == 2)
		{
			i = 6;
			length = this.barcodeBinary.length;
		}
			
		for(; i < length; ++i)
		{
			for(int n = 0; n < this.barcodeBinary[i].length; ++n)
			{
				if(this.barcodeBinary[i][n] == 0)
				{
					ig2.setPaint(Color.white);
				    ig2.setStroke(new BasicStroke(this.barWidth));
				}
				else
				{
					ig2.setPaint(Color.black);
					ig2.setStroke(new BasicStroke(this.barWidth));
				}
				  		  
				int pos = this.imgPixelPos;
				ig2.drawLine(pos,0,pos,this.barHeight);
				this.imgPixelPos += this.barWidth;
			}
		}
	}
	
	private void drawSpecial(int type)
	{
		
		/*
		Special Symbol Pattern
		Quite Zone 	000000000
		Lead / Trailer 	101
		Separator 	01010
		 */
		
		int[] quiteZone = {0,0,0,0,0,0,0,0,0};
		int[] leadtrail = {1,0,1};
		int[] separator = {0,1,0,1,0};
		int binaryArrLength = 0;
		int[] arr;
		
		if(type == 0)
		{
			binaryArrLength = quiteZone.length;
			arr = quiteZone;
		}
		else if(type == 1)
		{
			binaryArrLength = leadtrail.length;
			arr = leadtrail;
		}
		else
		{
			binaryArrLength = separator.length;
			arr = separator;
		}

		for(int n = 0; n < binaryArrLength; ++n)
		{
			if(arr[n] == 0)
				ig2.setPaint(Color.white);
			else
				ig2.setPaint(Color.black);
			  		  
			int pos = this.imgPixelPos;
			ig2.drawLine(pos,0,pos,this.barHeight);
			this.imgPixelPos += this.barWidth;
		}
	}
	
	public int calculateControlDigit(String ean)
	{
		int sum = 0;
		for(int i = 0; i < 12; ++i)
		{
			int val = charToInt(ean.charAt(i));
			if((i+1)%2 == 0)
				sum += val*3;
			else
				sum += val*1;	
		}

		return (10 - (sum % 10));
	}
	
	private int charToInt(char c)
	{
		return Integer.parseInt(String.valueOf(c));
	}
}

Using Java to connect with SSLSocket, trusting all certificates

So I wanted to make a little test client that would connect to a web server via SSL.
The problem is that in this experiment I don’t really care about the security, so I want the client to accept all certificates, even self signed ones and very old ones.
Credits to my good friend Maboroshi who helped me find the final solution that solved the last problem with the invalid certs.

So the first thing I did was to write the following.
This is a safe code that does NOT accept invalid certificates of any kind.

SAFE

import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import java.io.*;
import java.net.Socket;

public class test {	
    public static void main(String[] arstring) {
        try {
            //Connect without SSL
            Socket clientSocket = new Socket("google.com", 80);
            DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
            BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

            outToServer.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("No SSL - " + inFromServer.readLine());
            clientSocket.close();
        	
            //Connect with SSL
            SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("google.com", 443);

            DataOutputStream outToServerSSL = new DataOutputStream(sslsocket.getOutputStream());
            BufferedReader inFromServerSSL = new BufferedReader(new InputStreamReader(sslsocket.getInputStream()));

            outToServerSSL.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("SSL - " + inFromServerSSL.readLine());
            
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

The problem with the above code for me, was that when connecting to a server with an invalid certificate, I got the following error.

Exception in thread “main” javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)

Thus, that solution wont cut it!
I needed something else, so I tried to write some code that would accept all certs as well.
Please note that ALL of the code from here on is UNSAFE, and should NOT be used in a production environment.


Reference to solution

UNSAFE

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.Socket;
import javax.net.ssl.TrustManager;
import java.security.cert.X509Certificate;

public class ssl {
    public static void main(String[] arstring) {
    	
        try {
            //Connect without SSL
            Socket clientSocket = new Socket("google.com", 80);
            DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
            BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
 
            outToServer.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("No SSL - " + inFromServer.readLine());
            clientSocket.close();
               
            //Connect with SSL
            // Create a trust manager that does not validate certificate chains
    		TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
    				public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    					return null;
    				}
    				
    				@Override
    				public void checkClientTrusted(X509Certificate[] certs, String authType) {
    				}
    				
    				@Override
    				public void checkServerTrusted(X509Certificate[] certs, String authType) {
    				}
    			}
    		};

            // Install the all-trusting trust manager
    	    SSLContext sc = SSLContext.getInstance("SSL");
    	    sc.init(null, trustAllCerts, new java.security.SecureRandom());
            
            
            SSLSocketFactory sslsocketfactory = sc.getSocketFactory();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("google.com", 443);
 
            DataOutputStream outToServerSSL = new DataOutputStream(sslsocket.getOutputStream());
            BufferedReader inFromServerSSL = new BufferedReader(new InputStreamReader(sslsocket.getInputStream()));
 
            outToServerSSL.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("SSL - " + inFromServerSSL.readLine());
           
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

But, now this gives me a different error because this invalid certificate is broken/old!

javax.net.ssl.SSLProtocolException: no more data allowed for version 1 certificate
at sun.security.ssl.HandshakeMessage$CertificateMsg.(HandshakeMessage.java:431)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:153)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:609)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:545)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:963)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1208)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:674)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:119)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:133)
at java.io.DataOutputStream.writeBytes(DataOutputStream.java:276)
at ssl.main(ssl.java:93)
Caused by: java.security.cert.CertificateParsingException: no more data allowed for version 1 certificate
at sun.security.x509.X509CertInfo.parse(X509CertInfo.java:710)
at sun.security.x509.X509CertInfo.(X509CertInfo.java:169)
at sun.security.x509.X509CertImpl.parse(X509CertImpl.java:1751)
at sun.security.x509.X509CertImpl.(X509CertImpl.java:196)
at sun.security.provider.X509Factory.engineGenerateCertificate(X509Factory.java:107)
at java.security.cert.CertificateFactory.generateCertificate(CertificateFactory.java:322)
at sun.security.ssl.HandshakeMessage$CertificateMsg.(HandshakeMessage.java:429)
… 10 more

Ok, so this is where it got tricky to solve.
I googled around for some time but I couldn’t really find a solution (since usually this is not the kind of thing you want to do).

But then a friend of mine (Maboroshi), pointed me to this site

Which brought me to this final solution.
The only real change here was to use the BouncyCastle provider

Security.insertProviderAt(new BouncyCastleProvider(), 1);

Which can be found HERE

Another requirement that I came to think about, is that this change requires Java 6 since Java 7 doesn’t support MD2. There is a solution if you are running Java 7 though.

Just open this file

JDK_HOME/jre/lib/security/java.security

And comment out the following line

jdk.certpath.disabledAlgorithms=MD2

That should do the trick.
Solution was found HERE

UNSAFE

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import java.io.*;
import java.net.Socket;
import javax.net.ssl.TrustManager;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import java.security.cert.X509Certificate;
import java.security.Security;
 
public class ssl {
    public static void main(String[] arstring) {
    	Security.insertProviderAt(new BouncyCastleProvider(), 1);
    	
        try {
            //Connect without SSL

            Socket clientSocket = new Socket("google.com", 80);
            DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
            BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
 
            outToServer.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("No SSL - " + inFromServer.readLine());
            clientSocket.close();
               
            //Connect with SSL
            
            // Create a trust manager that does not validate certificate chains
    		TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
    				@Override
					public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    					return null;
    				}
    				
    				@Override
    				public void checkClientTrusted(X509Certificate[] certs, String authType) {
    				}
    				
    				@Override
    				public void checkServerTrusted(X509Certificate[] certs, String authType) {
    				}
    			}
    		};

    		// Install the all-trusting trust manager
    		SSLContext sc = SSLContext.getInstance("SSL");
    		sc.init(null, trustAllCerts, new java.security.SecureRandom());
            
            
            SSLSocketFactory sslsocketfactory = sc.getSocketFactory();
            SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("google.com", 443);
 
            DataOutputStream outToServerSSL = new DataOutputStream(sslsocket.getOutputStream());
            BufferedReader inFromServerSSL = new BufferedReader(new InputStreamReader(sslsocket.getInputStream()));
 
            outToServerSSL.writeBytes("GET / HTTP/1.1nHost: google.comnn");
            System.out.println("SSL - " + inFromServerSSL.readLine());
           
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }
}

Now I got the output I wanted 🙂

No SSL – HTTP/1.1 200 OK
SSL – HTTP/1.1 200 OK

Java – 7zip unzip example

Used one of the examples in the 7zip Java bindings documentation to make this (I just modified it a little bit to actually write the unzipped data to the correct files as well, instead of just listing them). My wife needed this example so it was just something quick we threw together for a little experiment, but I thought I could put it here in case someone finds it and needs it.

Example output

   Hash   |    Size    | Filename
----------+------------+---------
 CC27EAB9 |     199872 | oclHashcat-plus-0.14/cudaHashcat-plus32.bin
 B661FF80 |     202248 | oclHashcat-plus-0.14/cudaHashcat-plus64.bin
  BCFEDE0 |     224352 | oclHashcat-plus-0.14/oclHashcat-plus32.bin
 39C89963 |     218472 | oclHashcat-plus-0.14/oclHashcat-plus64.bin
 500CE011 |     222568 | oclHashcat-plus-0.14/vclHashcat-plus64.bin

Code

package unzip;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Arrays;

import net.sf.sevenzipjbinding.ExtractOperationResult;
import net.sf.sevenzipjbinding.ISequentialOutStream;
import net.sf.sevenzipjbinding.ISevenZipInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.SevenZipException;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchive;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;

public class unzip {
	public static void main(String[] args) {

        RandomAccessFile randomAccessFile = null;
        ISevenZipInArchive inArchive = null;
        
        try {
            randomAccessFile = new RandomAccessFile("oclHashcat-plus-0.14.7z", "r");
            inArchive = SevenZip.openInArchive(null, // autodetect archive type
                    new RandomAccessFileInStream(randomAccessFile));

            // Getting simple interface of the archive inArchive
            ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();

            System.out.println("   Hash   |    Size    | Filename");
            System.out.println("----------+------------+---------");

            for (final ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
                final int[] hash = new int[] { 0 };
                if (!item.isFolder()) {
                    ExtractOperationResult result;

                    final long[] sizeArray = new long[1];
                    result = item.extractSlow(new ISequentialOutStream() {
                        public int write(byte[] data) throws SevenZipException {
                        	
                        	//Write to file
                        	FileOutputStream fos;
							try {
								File file = new File(item.getPath());
	                        	file.getParentFile().mkdirs();
								fos = new FileOutputStream(file);
								fos.write(data);
	                        	fos.close();
	                        	
							} catch (FileNotFoundException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							} catch (IOException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}

                            hash[0] ^= Arrays.hashCode(data); // Consume data
                            sizeArray[0] += data.length;
                            return data.length; // Return amount of consumed data
                        }
                    });
                    if (result == ExtractOperationResult.OK) {
                        System.out.println(String.format("%9X | %10s | %s", // 
                                hash[0], sizeArray[0], item.getPath()));
                    } else {
                        System.err.println("Error extracting item: " + result);
                    }
                }
            }
        } catch (Exception e) {
            System.err.println("Error occurs: " + e);
            System.exit(1);
        } finally {
            if (inArchive != null) {
                try {
                    inArchive.close();
                } catch (SevenZipException e) {
                    System.err.println("Error closing archive: " + e);
                }
            }
            if (randomAccessFile != null) {
                try {
                    randomAccessFile.close();
                } catch (IOException e) {
                    System.err.println("Error closing file: " + e);
                }
            }
        }
	}
}