Passwort einer verschlüsselten PDF ermitteln

Vor einigen Tagen stand ich vor dem Problem ein PDF mit Vertragsunterlagen öffnen zu wollen, welches allerdings mit einem Passwort geschützt war. Und wie es Murphys Gesetz vorschreibt, hatte ich das entsprechende Passwort natürlich verlegt.

Mithilfe der Software PDFCrack, kann ein solches Passwort wieder hergestellt werden. Dazu muss die Software erst installiert werden, unter macOS z. B. mittels Homebrew:

brew install pdfcrack

Unter Linux steht PDFCrack über die entsprechenden Paketmanager ebenfalls zur Verfügung. Anschließend kann damit begonnen werden das Passwort zu ermitteln:

pdfcrack -f encrypted.pdf

PDFCrack beginnt nun damit alle möglichen Passwörter durchzuprobieren; bis schlussendlich das richtige Passwort gefunden wurde:

Security Handler: Standard
V: 2
R: 3
P: -1852
Length: 128
Encrypted Metadata: True
FileID: 382bbf6cdba9941cfd35bd21e6dbas0b68
U: 07556c324622f519b6838c213136129500000000000000000000000000000000
O: 9fb467e3c673228ac08571dab482b385da99ada024cf952ef8f8489d6f680d226
Average Speed: 84554.6 w/s. Current Word: 'O4eg'
Average Speed: 84246.6 w/s. Current Word: '9ojn'
Average Speed: 84455.9 w/s. Current Word: 'ZOou'

Wird der Prozess abgebrochen; so wird der aktuelle Status in der einer sav-Datei abgespeichert. Leider ist PDFCrack nicht Multithreading fähig; sodass es von mehreren Prozessorkernen leider nicht profitiert und der Vorgang somit entsprechend lange dauert.

Elasticsearch unter Ubuntu installieren

Elasticsearch ist eine Suchmaschine, welche in vielen Lösungen zum Einsatz kommt. Seit der Lizenzänderung vor ein paar Wochen ist Elasticsearch keine freie Software mehr, wird aber trotzdem in vielen Lösungen genutzt, welche noch nicht auf eventuelle freie Forks ausgewichen sind. Soll Elasticsearch unter Ubuntu installiert werden, so kann dies über den Paketmanager installiert werden. Im ersten Schritt muss das Paket apt-transport-https nachinstalliert werden:

apt install apt-transport-https

Anschließend kann die Fremdpaketquelle hinzugefügt werden:

echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-7.x.list

Diese wird benötigt, da Elasticsearch nicht in den offiziellen Paketquellen von Ubuntu enthalten ist. Damit die Installation über das Paket funktioniert, sollte der entsprechende Schlüssel hinzugefügt werden:

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | apt-key add -

Sollte beim Aufruf des Kommandos die Meldung:

E: gnupg, gnupg2 and gnupg1 do not seem to be installed, but one of them is required for this operation

erscheinen, muss das entsprechende Paket nachinstalliert werden:

apt install gnupg2

Anschließend kann Elasticsearch installiert werden:

apt-get update && sudo apt-get install elasticsearch

Standardmäßig ist Elasticsearch nach der Installation nur über den Localhost am Port 9200 erreichbar. Um dies zu ändern, muss die Konfiguration angepasst werden. Diese befindet sich im Ordner /etc/elasticsearch/ – sollen hier Änderungen vorgenommen werden, sollte die entsprechende Dokumentation zurate gezogen werden.

Erste Schritte mit Rust

Vor ein paar Tagen wollte ich die Programmiersprache Rust ausprobieren. Bei Rust handelt es sich um eine Sprache, welche syntaktisch stark an C angelehnt ist und besonderes Augenmerk auf Sicherheit legt. Die erste Frage, die sich mir stellte, ist, ob es für Rust, eine vernünftige IDE-Unterstützung existiert. Fündig geworden bin ich bei IntelliJ IDEA, welches nach Installation des Rust-Plugins zur Programmierung in der Sprache genutzt werden kann. Debugging von Rust-Programmen wird aus technischen Gründen nur in der JetBrains IDE CLion unterstützt, sodass hier einige Abstriche gemacht werden müssen. Neben der Rust-Integration für IntelliJ IDEA gibt es ebenfalls ein Plugin für Visual Studio Code, welches hier allerdings nicht weiter behandelt werden soll.

Mit dem passenden Plugin beherrscht IntelliJ IDEA Rust.

Neben der eigentlichen IDE wird Rust benötigt. Dieses kann über die offizielle Seite der Sprache bezogen werden. Nach der Installation kann in der IDE ein erstes Projekt angelegt werden. Anschließend findet sich in einem Ordner mit dem Namen src eine Datei mit dem Namen main.rs:

fn main() {
    println!("Hello, world!");
}

In dieser Datei findet sich ein minimales Hello world-Programm. Das Ausrufezeichen hinter dem println zeigt unter Rust an, das es sich um ein Makro handelt. Damit können Methoden und Makros einfach auseinander gehalten werden. Da ich Hello world-Programme immer etwas sinnfrei finde was das Lernen einer neuen Programmiersprache angeht, wollte ich für den ersten Versuch das Spiel Zahlenraten programmieren. Ziel des Spieles ist es eine Zahl zwischen 0 und 1000 zu erraten, welche der Rechner sich ausgedacht hat. Dazu muss im ersten Schritt eine Variable definiert werden, in welcher die zufällige Zahl gespeichert wird. Grundsätzlich sieht eine Variablendefinition und Deklaration unter Rust wie folgt aus:

let name: Typ = Wert;

Variablen in Rust sind immer Konstanten, wenn sie nicht explizit als veränderlich angegeben werden. Möglich ist dies mit dem Schlüsselwort mut:

let mut name: Typ = Wert;

Die Benennung von Variablen und anderen Elementen folgt in Rust einem bestimmten Schema. So werden Variablen snake_case benannt. Neben der Definition der Variable muss eine zufällige Zahl zwischen 0 und 1000 generiert werden. Dazu dient ein Zufallsgenerator, welcher über ein use eingebunden werden muss:

use rand::Rng;

Anschließend kann die Variable definiert und deklariert werden:

let number: u32 = rand::thread_rng().gen_range(0, 1000);

In diesem Fall wird ein vorzeichenloser Integer mit 32 Bit Breite als Datentyp definiert. Wird nun versucht das Rust-Programm zu kompilieren, so wird die Fehlermeldung:

error[E0432]: unresolved import `rand`
 --> src\main.rs:1:5
  |
1 | use rand::Rng;
  |     ^^^^ use of undeclared type or module `rand`

error[E0433]: failed to resolve: use of undeclared type or module `rand`
 --> src\main.rs:4:23
  |
4 |     let number: u32 = rand::thread_rng().gen_range(0, 1000);
  |                       ^^^^ use of undeclared type or module `rand`

error: aborting due to 2 previous errors

auftauchen. Grund hierfür ist, dass das genutzte Paket über den Paketmanager von Rust bezogen werden muss. Pakete werden über den Paketmanager Cargo bezogen. Die entsprechenden Pakete des offiziellen Repositorys sind unter crates.io zu finden. In der Datei Cargo.toml müssen die entsprechenden Abhängigkeiten eingebunden werden:

[dependencies]
rand = "0.7.3"
text_io = "0.1.8"

Neben dem Pseudozufallszahlengenerator, wurde gleich noch das Paket text_io eingebunden, welches später für die Eingabe von Text benötigt wird. Dieses Paket stellt hierbei das Makro read zur Verfügung, mit dessen Hilfe eine Eingabe realisiert werden kann:

let user_number: u32 = read!();

Damit sind die Grundlagen für das Spiel Zahlenraten gelegt und der Rest des Quellcodes ergibt praktisch sich von selbst:

use rand::Rng;
use text_io::read;

fn main() {

    let number: u32 = rand::thread_rng().gen_range(0, 1000);

    let mut running = true;

    println!("Ich habe mir eine Zahl zwischen 0 und 1000 ausgedacht.");

    while running {

        println!("Dein Vorschlag: ");
        let user_number: u32 = read!();

        if number > user_number {
            println!("Meine Zahl ist größer.");
        } else if number < user_number {
            println!("Meine Zahl ist kleiner.");
        } else {
            running = false;
        }
    }

    println!("Du hast die Zahl erraten. Es war die {}.", number);
}

Hervorzuheben ist, das unter Rust in if-Statements und Schleifen keine Klammern benötigt werden. Werden diese trotzdem genutzt, so warnt der Compiler entsprechend und bittet die überflüssigen Klammern zu entfernen.

Neben der entsprechenden Dokumentation auf der offiziellen Seite empfiehlt sich das Rust Cookbook als Quelle für die ersten Schritte unter Rust. Wer Rust einfach mal im Browser ausprobieren möchte, kann hierfür den Rust Playground nutzen.

Restic für Backups nutzen

Für Backups nutze ich seit vielen Jahren rsync-time-backup. Allerdings hörte ich in letzter Zeit viel gutes über die freie Software Restic. Restic selbst wird über GitHub entwickelt und ist unter der BSD-Lizenz in der Zweiklausel-Version lizenziert. Unter Linux und macOS kann Restic einfach über entsprechende Paketmanager installiert werden:

brew install restic

Restic arbeiten mit sogenannten Repositories. In einem Repository befindet sich das entsprechende Backup mit all seinen Versionen. Um ein solchen Repository anzugelegen wirde der Befehl:

restic init --repo ./

genutzt. Bei Restic ist jedes Backup automatisch verschlüsselt, sodass bereits beim Anlegen eines Backups ein entsprechendes Passwort vergeben werden muss. Die Daten werden mit AES, bei 256 Bit, verschlüsselt.

restic.net

Danach kann theoretisch mit dem ersten Backup begonnen werden:

restic -r /Volumes/Volume/ResticRepository backup /Users/User

In diesem Fall würde der Ordner /Users/User/ in das Restic-Repository gesichert. Bevor das Backup startet, muss das entsprechende Passwort des Repositorys eingegeben werden. Anschließend wird der Nutzer über den Fortschritt des Prozesses informiert:

repository 567f35fa opened successfully, password is correct
created new cache in /Users/User/Library/Caches/restic
[0:09] 10 files 4.296 MiB, total 451 files 224.551 MiB, 0 errors
/Users/User/System/btrfstune
/Users/User/System/busybox
...

Nach dem Abschluss des Backups erscheint eine entsprechende Meldung im Terminal:

Files:          25 new,     0 changed,     0 unmodified
Dirs:            2 new,     0 changed,     0 unmodified
Added to the repo: 616.694 KiB

processed 25 files, 615.493 KiB in 0:00
snapshot 6c0d7af6 saved

Nun verfügt der Nutzer über ein Backup Repository mit einem bzw. mehreren Snapshots. Die angelegten Snapshots können über dem Befehl:

restic -r /Volumes/Volume/ResticRepository snapshots

angezeigt werden. Der Nutzer erhält eine entsprechende Ausgabe im Terminal:

repository fd5947c7 opened successfully, password is correct
ID        Time                 Host        Tags        Paths
------------------------------------------------------------------------------
6c0d7af6  2020-01-05 10:09:15  Earth.local             /Users/User/System
31d3160f  2020-01-05 10:11:30  Earth.local             /Users/User/System
38d6cbca  2020-01-05 10:15:09  Earth.local             /Users/User/System
ea96fa22  2020-01-05 10:15:20  Earth.local             /Users/User/System
------------------------------------------------------------------------------
4 snapshots

Das beste Backup nutzt nichts, wenn es nicht wiederhergestellt werden kann. Dazu wird die Option restore genutzt:

restic -r /Volumes/Volume/ResticRepository restore 38d6cbca --target /Users/User/System

Anschließend wird der gewünschte Snapshop wieder hergestellt:

repository fd5947c7 opened successfully, password is correct
restoring  to /Users/User/System

Soll anstatt eines bestimmten Snapshot der letzte Snapshot wiederhergestellt werden so wird anstatt einer Snapshot-ID einfach latest als Wert angegeben. Soll nur eine einzelne Datei wiederhergestellt werden, ist das komplette zurückspielen eines Backup eher suboptimal. Für einen solchen Fall können die Snapshots im Dateisystem gemountet werden.

restic -r /Volumes/Volume/ResticRepository mount /Volumes/Volume/ResticRepositoryMounted

Anschließend wird das Repository im Dateisystem unter dem angegebenen Mountpoint eingebunden:

repository fd5947c7 opened successfully, password is correct
Now serving the repository at /Volumes/Volume/ResticRepositoryMounted
When finished, quit with Ctrl-c or umount the mountpoint.

Im Gegensatz zu den Befehlen zur Wiederherstellung des Backups muss beim Mounten keine Snapshot-ID angegeben werden. In der gemounteten Struktur werden stattdessen alle Snapshots angezeigt. Die gewünschte Datei zur Wiederherstellung kann somit gesucht und wiederhergestellt werden.

Beim Backup sollen in vielen Fällen bestimmte Dateien nicht gesichert werden. Dies können z.B. temporäre Dateien oder Caches sein. Um diese Datei vom Backup auszuschließen, kann ein sogenanntes Exclude File genutzt werden:

restic -r /Volumes/Volume/ResticRepository backup /Users/User/System --exclude-file="/Users/User/excludes.txt"

Eine solche Datei könnte z.B. wie folgt aussehen:

+ /etc/
+ /home/
+ /root/
+ /srv/
+ /usr/local/
+ /var/

- /*
- /var/cache/*
- /var/lib/lxcfs/*
- /var/log/*
- /var/tmp/*

Neben diesen Basisfunktionalitäten, verfügt Restic über weitere Funktionen, so z.B. zum Löschen alter Snapshots nach bestimmten Regeln. Alles in allem wirkt Restic für mich wie eine durchdachte Backup-Lösung, deren Nutzung durchaus ins Auge gefasst werden kann.

apt vs. apt-get

Wurde früher unter Debian oder Ubuntu ein Paket installiert, so wurde das Kommando apt-get dazu genutzt:

apt-get install mc

Seit Ubuntu 14.04 gibt es neben den Kommandos rund um apt-get, das Kommando apt. Ab Ubuntu 16.04 wurde offiziell empfohlen apt anstatt von apt-get und apt-cache zu nutzen. Da stellt sich natürlich die Frage wie sich die Kommandos voneinander unterscheiden? Bei den alten Kommandos musste für die Paketverwaltung mit apt-get und apt-cache gearbeitet werden, je nachdem welche Operation benötigt wurde. Die Installation wurde mit apt-get install vorgenommen. Für eine Suche über die Pakete wurde stattdessen apt-cache benötigt:

apt-cache search mc

Mit dem damals neu eingeführten Kommando apt gibt es nun ein einheitliches Interface für die Paketverwaltung:

apt install mc
apt search mc

Neben der Vereinheitlichung, bietet apt einige weitere Vorteile, so kann unter anderem der Fortschritt einer Operation angezeigt werden.

apt zeigt unter anderem den Fortschritt der Operation an

Im Grunde ist apt eine Zusammenführung der am häufigsten genutzten Kommandos zur Paketverwaltung unter einem Kommando. Bestimmte obskure Low-Level-Operationen, welche bei apt-get und apt-cache noch zu finden waren, wurden bei apt zugunsten der Benutzbarkeit weggelassen. Zusätzlich dazu sind die Standardeinstellungen von apt sinnvoller gesetzt.