Skip to content

Instantly share code, notes, and snippets.

@dhondta
Last active July 31, 2023 22:16
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Embed
What would you like to do?
Tinyscript steganography tool based on base32/64 padding

Paddinganograph

This Tinyscript-based tool allows to unhide data hidden in base32/base64 strings. It can take a PNG or JPG in input to retrieve an EXIF value as the input data.

This can be installed using:

$ pip install tinyscript
$ tsm install paddinganograph
$ paddinganograph -e base64 -f Comment -s . < test.jpg
[...]
$ paddinganograph -e base64 -f Comment -s . < test.jpg | paddinganograph -e base32
[...]

This tool is especially useful in the use cases hereafter.

Retrieve hidden data from an image using Base32/64 padding

Select the "Comment" field, split its value with the separator "." and extract hidden data:

$ paddinganograph -s "." -f "Comment" < image.jpg
[...]
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import math
from tinyscript import *
__author__ = "Alexandre D'Hondt"
__version__ = "1.5"
__copyright__ = ("A. D'Hondt", 2019)
__license__ = "gpl-3.0"
__reference__ = "https://inshallhack.org/paddinganography/"
__examples__ = ["-s . -f \"Comment\" < image.jpg > base32.enc", "-e base32 < base32.enc"]
__docformat__ = "md"
__doc__ = """
*Paddinganograph* allows to unhide data hidden in base32/base64 strings. It can take a PNG or JPG in input to retrieve an EXIF value as the input data.
"""
SCRIPTNAME_FORMAT = "none"
DEF_BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
DEF_BASE32 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
def exif(raw_data, key):
t = ts.TempPath().tempfile().name
with open(str(t), 'wb') as f:
f.write(raw_data)
logger.debug("Getting EXIF data...")
exif = subprocess.check_output(["exiftool", str(t)])
exif = codext.decode(exif, "utf-8")
exif = {l.split(':', 1)[0].strip(): l.split(':', 1)[1].strip() for l in exif.split('\n') if l.strip() != ""}
return exif if not key else exif[key]
def unhide(encoded, encoding="base64", charset=None, sep=".", pad="=", n_pad=8):
try:
charset = (charset or globals()["DEF_{}".format(encoding.upper())]).strip()
except NameError:
raise ValueError("Bad encoding")
logger.debug("Unhidding data...")
bits = b("")
for token in b(encoded).split(b(sep)):
bits += unhide_bits(token.strip(), charset, pad, n_pad) or b("")
return b("").join(ts.bin2str(bits[i:i+8]) for i in range(0, len(bits), 8))
def unhide_bits(encoded, charset, pad="=", n_pad=8):
def __gcd(a,b):
while b > 0:
a, b = b, a % b
return a
padding = encoded.count(b(pad))
n_repr = int(math.ceil(math.log(len(charset), 2)))
w_len = n_repr * n_pad / __gcd(n_repr, n_pad)
n_char = int(math.ceil(float(w_len) / n_repr))
if encoded == "" or len(encoded) % n_char != 0 or padding == 0:
return
unused = {n: int(w_len - n * n_repr) % n_pad for n in range(n_char)}
b_val = bin(b(charset).index(encoded.rstrip(b(pad))[-1]))[2:].zfill(n_repr)
return b(b_val[-unused[padding]:])
if __name__ == '__main__':
parser.add_argument("-c", "--charset", help="characters set")
parser.add_argument("-e", "--encoding", choices=["base32", "base64"], default="base64", help="character encoding")
parser.add_argument("-f", "--exif-field", dest="exif", default="Comment", help="EXIF metadata field to be selected")
parser.add_argument("-p", "--padding-char", default="=", help="padding character")
parser.add_argument("-s", "--separator", default="\n", help="base-encoded token separator")
initialize()
data = b("").join(l for l in ts.stdin_pipe())
if data.startswith(b("\x89PNG")) or data.startswith(b("\xff\xd8\xff\xe0")):
try:
data = exif(data, args.exif)
except KeyError:
logger.error("No EXIF field '%s'" % args.exif)
sys.exit(1)
print(ensure_str(unhide(data, args.encoding, args.charset, args.separator)))
@Fufu-btw
Copy link

Fufu-btw commented Aug 12, 2022

@Tom-Weber Merci d'avoir rapporté ce problème. Cette erreur provient de MarkdownViewer (mdv). Dans la dernière version de Tinyscript, au lieu de charger mdv sous Python2 et mdv3 (une version alternative qui fixe la compatibilité) sous Python3, j'ai rétabli un seul import de mdv (et plus mdv3 car cette compatibilité semble avoir été fixée récemment dans le paquet de MarkdownViewer. Par conséquent, si pip install --upgrade mdv et pip install --upgrade tinyscript ne résolvent pas le problème, c'est que le problème de compatibilité n'est toujours pas fixé. Peux-tu essayer ceci et tester de ton côté, stp ?

Après pip install --upgrade mdv et pip install --upgrade tinyscript toutes les dépendances étaient déjà ok + modules à jour.
J'ai relancé le script, même erreur : ImportError: cannot import name 'etree' from 'markdown.util' (/usr/lib/python3/dist-packages/markdown/util.py)

Personnelement pour l'erreur markdown j'ai pu résoudre en rétrogradant la version pour installer markdown-3.3.7 via pip install markdown==3.3.7 En revanche quand je fais un test sur mon fichier j'ai l'erreur suivante:

#python3 paddinography.py -s "." -f "Comment" < ch15.jpg                                                   
Traceback (most recent call last):
  File "/home/kali/Desktop/Rootme/ch15/paddinography.py", line 83, in <module>
    data = exif(data, args.exif)
  File "/home/kali/Desktop/Rootme/ch15/paddinography.py", line 29, in exif
    t = TempPath().tempfile().name
NameError: name 'TempPath' is not defined

(Il y a bien un "Comment" EXIF dans mon fichier) Merci pour ton aide.

EDIT : j'ai cette erreur si je call le script paddinograph.py mais si j'appelle le bin tout fonctionne depuis ton upgrade du code :)

Après pip install markdown==3.3.7, tout est ok ! Merci à vous deux pour votre aide :)

Il ne serait pas plus simple d'avoir un projet python avec les dépendance utilisées ? @dhondta Cela pourrait éviter toutes les erreurs concernant les version !

Merci d'avance !

@dhondta
Copy link
Author

dhondta commented Aug 13, 2022

Salut @Tom-Weber !
Merci pour ces explications.
Après pip3 install --upgrade markdown, j'ai :

$ pip show markdown
Name: Markdown
Version: 3.4.1
Summary: Python implementation of Markdown.
...

Et j'obtiens bien l'erreur que tu as rapportée :

$ paddinganograph --help
Traceback (most recent call last):
[...]
ImportError: cannot import name 'etree' from 'markdown.util' (/home/morfal/.local/lib/python3.8/site-packages/markdown/util.py)

Le problème vient bien de mdv qui n'est pas compatible avec markdown>=3.4 apparemment.
Je peux épingler markdown==3.3.7 temporairement pour résoudre le problème.
Merci en tout cas.

@harmtemolder
Copy link

Can you explain what exactly happens in the unhide_bits function?

@Blasci
Copy link

Blasci commented May 31, 2023

Bonjour a tous,

Si comme moi vous avez cette erreur :

NameError: name 'codecs' is not defined. Did you mean: 'code'?

Ajouter simplement la ligne suivante dans le fichier

import codecs

Salutation ✋

@dhondta
Copy link
Author

dhondta commented Jun 2, 2023

@Blasci Merci pour cette remarque !
Le problème vient du fait que j'ai retiré codecs des pré-imports dans Tinyscript, sur lequel s'appuie le présent outil.
J'y ai donc remplacé codecs par codext (qui est son extension).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment